UPSTREAM BASELINE: nextcloud/spreed v22.0.12 (без изменений)
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled
Источник: https://github.com/nextcloud/spreed/archive/refs/tags/v22.0.12.tar.gz С этого коммита ветка официального Nextcloud Talk отрезана (решение владельца 2026-07-06). Все дальнейшие изменения — только наши; версии релизов: 22.0.12-f7.N.
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Activity\Provider;
|
||||
|
||||
use OCA\Talk\Activity\Provider\Base;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\AvatarService;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCP\Activity\Exceptions\UnknownActivityException;
|
||||
use OCP\Activity\IEvent;
|
||||
use OCP\Activity\IManager;
|
||||
use OCP\Federation\ICloudIdManager;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\L10N\IFactory;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
/**
|
||||
* Class BaseTest
|
||||
*
|
||||
* @package OCA\Talk\Tests\php\Activity
|
||||
*/
|
||||
class BaseTest extends TestCase {
|
||||
protected IFactory&MockObject $l10nFactory;
|
||||
protected IURLGenerator&MockObject $url;
|
||||
protected Config&MockObject $config;
|
||||
protected IManager&MockObject $activityManager;
|
||||
protected IUserManager&MockObject $userManager;
|
||||
protected ICloudIdManager&MockObject $cloudIdManager;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected AvatarService&MockObject $avatarService;
|
||||
protected Manager&MockObject $manager;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->l10nFactory = $this->createMock(IFactory::class);
|
||||
$this->url = $this->createMock(IURLGenerator::class);
|
||||
$this->config = $this->createMock(Config::class);
|
||||
$this->activityManager = $this->createMock(IManager::class);
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->cloudIdManager = $this->createMock(ICloudIdManager::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->avatarService = $this->createMock(AvatarService::class);
|
||||
$this->manager = $this->createMock(Manager::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $methods
|
||||
*/
|
||||
protected function getProvider(array $methods = []): Base&MockObject {
|
||||
$methods[] = 'parse';
|
||||
return $this->getMockBuilder(Base::class)
|
||||
->setConstructorArgs([
|
||||
$this->l10nFactory,
|
||||
$this->url,
|
||||
$this->config,
|
||||
$this->activityManager,
|
||||
$this->userManager,
|
||||
$this->cloudIdManager,
|
||||
$this->participantService,
|
||||
$this->avatarService,
|
||||
$this->manager,
|
||||
])
|
||||
->onlyMethods($methods)
|
||||
->getMock();
|
||||
}
|
||||
|
||||
|
||||
public static function dataPreParse(): array {
|
||||
return [
|
||||
['other', false, true, true],
|
||||
['spreed', false, true, true],
|
||||
['spreed', true, true, true],
|
||||
['spreed', true, false, false],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataPreParse')]
|
||||
public function testPreParse(string $appId, bool $hasUser, bool $disabledForUser, bool $willThrowException): void {
|
||||
$user = $hasUser ? $this->createMock(IUser::class) : null;
|
||||
|
||||
/** @var IEvent&MockObject $event */
|
||||
$event = $this->createMock(IEvent::class);
|
||||
$event->expects($this->once())
|
||||
->method('getApp')
|
||||
->willReturn($appId);
|
||||
|
||||
if ($willThrowException) {
|
||||
$this->expectException(UnknownActivityException::class);
|
||||
}
|
||||
$event->expects($this->exactly($willThrowException ? 0 : 1))
|
||||
->method('setIcon')
|
||||
->willReturnSelf();
|
||||
|
||||
if ($user) {
|
||||
$this->config
|
||||
->method('isDisabledForUser')
|
||||
->with($user)
|
||||
->willReturn($disabledForUser);
|
||||
$this->userManager
|
||||
->method('get')
|
||||
->with('user')
|
||||
->willReturn($user);
|
||||
$event->expects($this->once())
|
||||
->method('getAffectedUser')
|
||||
->willReturn('user');
|
||||
}
|
||||
|
||||
$provider = $this->getProvider();
|
||||
static::invokePrivate($provider, 'preParse', [$event]);
|
||||
}
|
||||
|
||||
public function testPreParseThrows(): void {
|
||||
/** @var IEvent&MockObject $event */
|
||||
$event = $this->createMock(IEvent::class);
|
||||
$event->expects($this->once())
|
||||
->method('getApp')
|
||||
->willReturn('activity');
|
||||
$provider = $this->getProvider();
|
||||
$this->expectException(UnknownActivityException::class);
|
||||
static::invokePrivate($provider, 'preParse', [$event]);
|
||||
}
|
||||
|
||||
public static function dataSetSubject(): array {
|
||||
return [
|
||||
['No placeholder', [], 'No placeholder'],
|
||||
['This has one {placeholder}', ['placeholder' => ['name' => 'foobar']], 'This has one foobar'],
|
||||
['This has {number} {placeholders}', ['number' => ['name' => 'two'], 'placeholders' => ['name' => 'foobars']], 'This has two foobars'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataSetSubject')]
|
||||
public function testSetSubject(string $subject, array $parameters, string $parsedSubject): void {
|
||||
$provider = $this->getProvider();
|
||||
|
||||
$event = $this->createMock(IEvent::class);
|
||||
$event->expects($this->once())
|
||||
->method('setParsedSubject')
|
||||
->with($parsedSubject)
|
||||
->willReturnSelf();
|
||||
$event->expects($this->once())
|
||||
->method('setRichSubject')
|
||||
->with($subject, $parameters)
|
||||
->willReturnSelf();
|
||||
|
||||
self::invokePrivate($provider, 'setSubjects', [$event, $subject, $parameters]);
|
||||
}
|
||||
|
||||
public static function dataGetRoom(): array {
|
||||
return [
|
||||
[Room::TYPE_ONE_TO_ONE, 23, 'private-call', 'private-call', 'one2one'],
|
||||
[Room::TYPE_GROUP, 42, 'group-call', 'group-call', 'group'],
|
||||
[Room::TYPE_PUBLIC, 128, 'public-call', 'public-call', 'public'],
|
||||
[Room::TYPE_ONE_TO_ONE, 23, '', 'a conversation', 'one2one'],
|
||||
[Room::TYPE_GROUP, 42, '', 'a conversation', 'group'],
|
||||
[Room::TYPE_PUBLIC, 128, '', 'a conversation', 'public'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataGetRoom')]
|
||||
public function testGetRoom(int $type, int $id, string $name, string $expectedName, string $expectedType): void {
|
||||
$provider = $this->getProvider();
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->expects($this->once())
|
||||
->method('getType')
|
||||
->willReturn($type);
|
||||
$room->expects($this->once())
|
||||
->method('getId')
|
||||
->willReturn($id);
|
||||
$room->expects($this->once())
|
||||
->method('getDisplayName')
|
||||
->with('user')
|
||||
->willReturn($expectedName);
|
||||
$room->expects($this->once())
|
||||
->method('getToken')
|
||||
->willReturn('token');
|
||||
|
||||
$this->url->expects($this->once())
|
||||
->method('linkToRouteAbsolute')
|
||||
->with('spreed.Page.showCall', ['token' => 'token'])
|
||||
->willReturn('url');
|
||||
|
||||
$this->assertEquals([
|
||||
'type' => 'call',
|
||||
'id' => $id,
|
||||
'name' => $expectedName,
|
||||
'call-type' => $expectedType,
|
||||
'link' => 'url',
|
||||
'icon-url' => '',
|
||||
], self::invokePrivate($provider, 'getRoom', [$room, 'user']));
|
||||
}
|
||||
|
||||
public static function dataGetUser(): array {
|
||||
return [
|
||||
['test', true, 'Test'],
|
||||
['foo', false, 'foo'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataGetUser')]
|
||||
public function testGetUser(string $uid, bool $validUser, string $name): void {
|
||||
$provider = $this->getProvider();
|
||||
|
||||
if ($validUser) {
|
||||
$this->userManager->expects($this->once())
|
||||
->method('getDisplayName')
|
||||
->with($uid)
|
||||
->willReturn($name);
|
||||
} else {
|
||||
$this->userManager->expects($this->once())
|
||||
->method('getDisplayName')
|
||||
->with($uid)
|
||||
->willReturn(null);
|
||||
}
|
||||
|
||||
$this->assertSame([
|
||||
'type' => 'user',
|
||||
'id' => $uid,
|
||||
'name' => $name,
|
||||
], self::invokePrivate($provider, 'getUser', [$uid]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Activity\Provider;
|
||||
|
||||
use OCA\Talk\Activity\Provider\Invitation;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Exceptions\RoomNotFoundException;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\AvatarService;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCP\Activity\Exceptions\UnknownActivityException;
|
||||
use OCP\Activity\IEvent;
|
||||
use OCP\Activity\IManager;
|
||||
use OCP\Federation\ICloudIdManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\L10N\IFactory;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
/**
|
||||
* Class InvitationTest
|
||||
*
|
||||
* @package OCA\Talk\Tests\php\Activity
|
||||
*/
|
||||
class InvitationTest extends TestCase {
|
||||
protected IFactory&MockObject $l10nFactory;
|
||||
protected IURLGenerator&MockObject $url;
|
||||
protected Config&MockObject $config;
|
||||
protected IManager&MockObject $activityManager;
|
||||
protected IUserManager&MockObject $userManager;
|
||||
protected ICloudIdManager&MockObject $cloudIdManager;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected AvatarService&MockObject $avatarService;
|
||||
protected Manager&MockObject $manager;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->l10nFactory = $this->createMock(IFactory::class);
|
||||
$this->url = $this->createMock(IURLGenerator::class);
|
||||
$this->config = $this->createMock(Config::class);
|
||||
$this->activityManager = $this->createMock(IManager::class);
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->cloudIdManager = $this->createMock(ICloudIdManager::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->avatarService = $this->createMock(AvatarService::class);
|
||||
$this->manager = $this->createMock(Manager::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $methods
|
||||
* @return Invitation|MockObject
|
||||
*/
|
||||
protected function getProvider(array $methods = []) {
|
||||
if (!empty($methods)) {
|
||||
return $this->getMockBuilder(Invitation::class)
|
||||
->setConstructorArgs([
|
||||
$this->l10nFactory,
|
||||
$this->url,
|
||||
$this->config,
|
||||
$this->activityManager,
|
||||
$this->userManager,
|
||||
$this->cloudIdManager,
|
||||
$this->participantService,
|
||||
$this->avatarService,
|
||||
$this->manager,
|
||||
])
|
||||
->onlyMethods($methods)
|
||||
->getMock();
|
||||
}
|
||||
return new Invitation(
|
||||
$this->l10nFactory,
|
||||
$this->url,
|
||||
$this->config,
|
||||
$this->activityManager,
|
||||
$this->userManager,
|
||||
$this->cloudIdManager,
|
||||
$this->participantService,
|
||||
$this->avatarService,
|
||||
$this->manager
|
||||
);
|
||||
}
|
||||
|
||||
public function testParseThrowsWrongSubject(): void {
|
||||
/** @var IEvent&MockObject $event */
|
||||
$event = $this->createMock(IEvent::class);
|
||||
$event->expects($this->once())
|
||||
->method('getApp')
|
||||
->willReturn('spreed');
|
||||
$event->expects($this->once())
|
||||
->method('getSubject')
|
||||
->willReturn('call');
|
||||
$event->expects($this->once())
|
||||
->method('getAffectedUser')
|
||||
->willReturn('user');
|
||||
|
||||
$user = $this->createMock(IUser::class);
|
||||
$this->userManager->expects($this->once())
|
||||
->method('get')
|
||||
->with('user')
|
||||
->willReturn($user);
|
||||
$this->config->expects($this->once())
|
||||
->method('isDisabledForUser')
|
||||
->with($user)
|
||||
->willReturn(false);
|
||||
|
||||
$provider = $this->getProvider();
|
||||
$this->expectException(UnknownActivityException::class);
|
||||
$provider->parse('en', $event);
|
||||
}
|
||||
|
||||
public static function dataParse(): array {
|
||||
return [
|
||||
['en', true, ['room' => 23, 'user' => 'test1'], ['actor' => ['actor-data'], 'call' => ['call-data']]],
|
||||
['de', false, ['room' => 42, 'user' => 'test2'], ['actor' => ['actor-data'], 'call' => ['call-unknown']]],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataParse')]
|
||||
public function testParse(string $lang, bool $roomExists, array $params, array $expectedParams): void {
|
||||
$provider = $this->getProvider(['setSubjects', 'getUser', 'getRoom', 'getFormerRoom']);
|
||||
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
$l->expects($this->any())
|
||||
->method('t')
|
||||
->willReturnCallback(function ($text, $parameters = []) {
|
||||
return vsprintf($text, $parameters);
|
||||
});
|
||||
|
||||
/** @var IEvent&MockObject $event */
|
||||
$event = $this->createMock(IEvent::class);
|
||||
$event->expects($this->once())
|
||||
->method('getApp')
|
||||
->willReturn('spreed');
|
||||
$event->expects($this->once())
|
||||
->method('getSubject')
|
||||
->willReturn('invitation');
|
||||
$event->expects($this->once())
|
||||
->method('getSubjectParameters')
|
||||
->willReturn($params);
|
||||
$event->expects($this->exactly($roomExists ? 2 : 1))
|
||||
->method('getAffectedUser')
|
||||
->willReturn('user');
|
||||
|
||||
$user = $this->createMock(IUser::class);
|
||||
$this->userManager->expects($this->once())
|
||||
->method('get')
|
||||
->with('user')
|
||||
->willReturn($user);
|
||||
$this->config->expects($this->once())
|
||||
->method('isDisabledForUser')
|
||||
->with($user)
|
||||
->willReturn(false);
|
||||
|
||||
if ($roomExists) {
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('getRoomById')
|
||||
->with($params['room'])
|
||||
->willReturn($room);
|
||||
|
||||
$provider->expects($this->once())
|
||||
->method('getRoom')
|
||||
->with($room, 'user')
|
||||
->willReturn(['call-data']);
|
||||
$provider->expects($this->never())
|
||||
->method('getFormerRoom');
|
||||
} else {
|
||||
$this->manager->expects($this->once())
|
||||
->method('getRoomById')
|
||||
->with($params['room'])
|
||||
->willThrowException(new RoomNotFoundException());
|
||||
|
||||
$provider->expects($this->never())
|
||||
->method('getRoom');
|
||||
$provider->expects($this->once())
|
||||
->method('getFormerRoom')
|
||||
->with($l)
|
||||
->willReturn(['call-unknown']);
|
||||
}
|
||||
|
||||
$this->l10nFactory->expects($this->once())
|
||||
->method('get')
|
||||
->with('spreed', $lang)
|
||||
->willReturn($l);
|
||||
|
||||
$provider->expects($this->once())
|
||||
->method('getUser')
|
||||
->with($params['user'])
|
||||
->willReturn(['actor-data']);
|
||||
$provider->expects($this->once())
|
||||
->method('setSubjects')
|
||||
->with($event, '{actor} invited you to {call}', $expectedParams);
|
||||
$provider->expects($this->once())
|
||||
->method('getUser')
|
||||
->with($params['user'])
|
||||
->willReturn(['actor-data']);
|
||||
|
||||
$provider->parse($lang, $event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Activity;
|
||||
|
||||
use OCA\Talk\Activity\Setting;
|
||||
use OCP\Activity\ISetting;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Test\TestCase;
|
||||
|
||||
class SettingTest extends TestCase {
|
||||
public static function dataSettings(): array {
|
||||
return [
|
||||
[Setting::class],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataSettings')]
|
||||
public function testImplementsInterface(string $settingClass): void {
|
||||
$setting = \OCP\Server::get($settingClass);
|
||||
$this->assertInstanceOf(ISetting::class, $setting);
|
||||
}
|
||||
|
||||
#[DataProvider('dataSettings')]
|
||||
public function testGetIdentifier(string $settingClass): void {
|
||||
/** @var ISetting $setting */
|
||||
$setting = \OCP\Server::get($settingClass);
|
||||
$this->assertIsString($setting->getIdentifier());
|
||||
}
|
||||
|
||||
#[DataProvider('dataSettings')]
|
||||
public function testGetName(string $settingClass): void {
|
||||
/** @var ISetting $setting */
|
||||
$setting = \OCP\Server::get($settingClass);
|
||||
$this->assertIsString($setting->getName());
|
||||
}
|
||||
|
||||
#[DataProvider('dataSettings')]
|
||||
public function testGetPriority(string $settingClass): void {
|
||||
/** @var ISetting $setting */
|
||||
$setting = \OCP\Server::get($settingClass);
|
||||
$priority = $setting->getPriority();
|
||||
$this->assertIsInt($setting->getPriority());
|
||||
$this->assertGreaterThanOrEqual(0, $priority);
|
||||
$this->assertLessThanOrEqual(100, $priority);
|
||||
}
|
||||
|
||||
#[DataProvider('dataSettings')]
|
||||
public function testCanChangeStream(string $settingClass): void {
|
||||
/** @var ISetting $setting */
|
||||
$setting = \OCP\Server::get($settingClass);
|
||||
$this->assertIsBool($setting->canChangeStream());
|
||||
}
|
||||
|
||||
#[DataProvider('dataSettings')]
|
||||
public function testIsDefaultEnabledStream(string $settingClass): void {
|
||||
/** @var ISetting $setting */
|
||||
$setting = \OCP\Server::get($settingClass);
|
||||
$this->assertIsBool($setting->isDefaultEnabledStream());
|
||||
}
|
||||
|
||||
#[DataProvider('dataSettings')]
|
||||
public function testCanChangeMail(string $settingClass): void {
|
||||
/** @var ISetting $setting */
|
||||
$setting = \OCP\Server::get($settingClass);
|
||||
$this->assertIsBool($setting->canChangeMail());
|
||||
}
|
||||
|
||||
#[DataProvider('dataSettings')]
|
||||
public function testIsDefaultEnabledMail(string $settingClass): void {
|
||||
/** @var ISetting $setting */
|
||||
$setting = \OCP\Server::get($settingClass);
|
||||
$this->assertIsBool($setting->isDefaultEnabledMail());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\BackgroundJob;
|
||||
|
||||
use OCA\Talk\BackgroundJob\CheckHostedSignalingServer;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Service\HostedSignalingServerService;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\IGroup;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\Notification\IManager;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class CheckHostedSignalingServerTest extends TestCase {
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected HostedSignalingServerService&MockObject $hostedSignalingServerService;
|
||||
protected IConfig&MockObject $config;
|
||||
protected IManager&MockObject $notificationManager;
|
||||
protected IGroupManager&MockObject $groupManager;
|
||||
protected IURLGenerator&MockObject $urlGenerator;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
protected Config&MockObject $talkConfig;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->hostedSignalingServerService = $this->createMock(HostedSignalingServerService::class);
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->notificationManager = $this->createMock(IManager::class);
|
||||
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->talkConfig = $this->createMock(Config::class);
|
||||
}
|
||||
|
||||
public function getBackgroundJob(): CheckHostedSignalingServer {
|
||||
return new CheckHostedSignalingServer(
|
||||
$this->timeFactory,
|
||||
$this->hostedSignalingServerService,
|
||||
$this->config,
|
||||
$this->notificationManager,
|
||||
$this->groupManager,
|
||||
$this->urlGenerator,
|
||||
$this->logger,
|
||||
$this->talkConfig
|
||||
);
|
||||
}
|
||||
|
||||
public function testRunWithNoChange(): void {
|
||||
$backgroundJob = $this->getBackgroundJob();
|
||||
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['spreed', 'hosted-signaling-server-account-id', '', 'my-account-id'],
|
||||
['spreed', 'hosted-signaling-server-account', '{}', '{"status": "pending"}']
|
||||
]);
|
||||
|
||||
$this->hostedSignalingServerService->expects($this->once())
|
||||
->method('fetchAccountInfo')
|
||||
->willReturn(['status' => 'pending']);
|
||||
|
||||
self::invokePrivate($backgroundJob, 'run', ['']);
|
||||
}
|
||||
|
||||
public function testRunWithPendingToActiveChange(): void {
|
||||
$backgroundJob = $this->getBackgroundJob();
|
||||
$newStatus = [
|
||||
'status' => 'active',
|
||||
'signaling' => [
|
||||
'url' => 'signaling-url',
|
||||
'secret' => 'signaling-secret',
|
||||
],
|
||||
];
|
||||
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['spreed', 'hosted-signaling-server-account-id', '', 'my-account-id'],
|
||||
['spreed', 'hosted-signaling-server-account', '{}', '{"status": "pending"}']
|
||||
]);
|
||||
$this->config->expects($this->once())
|
||||
->method('deleteAppValue')
|
||||
->with('spreed', 'signaling_mode');
|
||||
|
||||
$expectedCalls = [
|
||||
['spreed', 'signaling_servers', '{"servers":[{"server":"signaling-url","verify":true}],"secret":"signaling-secret"}'],
|
||||
['spreed', 'hosted-signaling-server-account', json_encode($newStatus)],
|
||||
];
|
||||
|
||||
$i = 0;
|
||||
$this->config->expects($this->exactly(count($expectedCalls)))
|
||||
->method('setAppValue')
|
||||
->willReturnCallback(function () use ($expectedCalls, &$i): void {
|
||||
$this->assertArrayHasKey($i, $expectedCalls);
|
||||
$this->assertSame($expectedCalls[$i], func_get_args());
|
||||
$i++;
|
||||
});
|
||||
|
||||
$group = $this->createMock(IGroup::class);
|
||||
$this->groupManager->expects($this->once())
|
||||
->method('get')
|
||||
->with('admin')
|
||||
->willReturn($group);
|
||||
$group->expects($this->once())
|
||||
->method('getUsers')
|
||||
->willReturn([]);
|
||||
|
||||
$this->hostedSignalingServerService->expects($this->once())
|
||||
->method('fetchAccountInfo')
|
||||
->willReturn($newStatus);
|
||||
|
||||
self::invokePrivate($backgroundJob, 'run', ['']);
|
||||
}
|
||||
|
||||
public function testRunWithPendingToActiveIncludingStunAndTurn(): void {
|
||||
$backgroundJob = $this->getBackgroundJob();
|
||||
$newStatus = [
|
||||
'status' => 'active',
|
||||
'signaling' => [
|
||||
'url' => 'signaling-url',
|
||||
'secret' => 'signaling-secret',
|
||||
],
|
||||
'stun' => [
|
||||
'servers' => [
|
||||
'stun.domain.invalid:443',
|
||||
'stun.domain.invalid:3478',
|
||||
],
|
||||
],
|
||||
'turn' => [
|
||||
'servers' => [
|
||||
[
|
||||
'server' => 'turn1.domain.invalid:443',
|
||||
'secret' => 'turn-secret',
|
||||
'schemes' => ['turns', 'turn'],
|
||||
'protocols' => ['tcp', 'udp'],
|
||||
],
|
||||
[
|
||||
'server' => 'turn2.domain.invalid:443',
|
||||
'secret' => 'other-turn-secret',
|
||||
'schemes' => ['turns'],
|
||||
'protocols' => ['tcp'],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->config
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['spreed', 'hosted-signaling-server-account-id', '', 'my-account-id'],
|
||||
['spreed', 'hosted-signaling-server-account', '{}', '{"status": "pending"}']
|
||||
]);
|
||||
$this->config->expects($this->once())
|
||||
->method('deleteAppValue')
|
||||
->with('spreed', 'signaling_mode');
|
||||
|
||||
$expectedCalls = [
|
||||
['spreed', 'signaling_servers', '{"servers":[{"server":"signaling-url","verify":true}],"secret":"signaling-secret"}'],
|
||||
['spreed', 'stun_servers', '["stun.domain.invalid:443","stun.domain.invalid:3478"]'],
|
||||
['spreed', 'turn_servers', '[{"server":"turn1.domain.invalid:443","secret":"turn-secret","schemes":"turn,turns","protocols":"udp,tcp"},{"server":"turn2.domain.invalid:443","secret":"other-turn-secret","schemes":"turns","protocols":"tcp"}]'],
|
||||
['spreed', 'hosted-signaling-server-account', json_encode($newStatus)],
|
||||
];
|
||||
|
||||
$i = 0;
|
||||
$this->config->expects($this->exactly(count($expectedCalls)))
|
||||
->method('setAppValue')
|
||||
->willReturnCallback(function () use ($expectedCalls, &$i): void {
|
||||
$this->assertArrayHasKey($i, $expectedCalls);
|
||||
$this->assertSame($expectedCalls[$i], func_get_args());
|
||||
$i++;
|
||||
});
|
||||
|
||||
$group = $this->createMock(IGroup::class);
|
||||
$this->groupManager->expects($this->once())
|
||||
->method('get')
|
||||
->with('admin')
|
||||
->willReturn($group);
|
||||
$group->expects($this->once())
|
||||
->method('getUsers')
|
||||
->willReturn([]);
|
||||
|
||||
$this->hostedSignalingServerService->expects($this->once())
|
||||
->method('fetchAccountInfo')
|
||||
->willReturn($newStatus);
|
||||
|
||||
self::invokePrivate($backgroundJob, 'run', ['']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\BackgroundJob;
|
||||
|
||||
use OCA\Talk\BackgroundJob\LockInactiveRooms;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\RoomService;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class LockInactiveRoomsTest extends TestCase {
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected RoomService&MockObject $roomService;
|
||||
private Config&MockObject $appConfig;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
private LockInactiveRooms $job;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->roomService = $this->createMock(RoomService::class);
|
||||
$this->appConfig = $this->createMock(Config::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->job = new LockInactiveRooms($this->timeFactory,
|
||||
$this->roomService,
|
||||
$this->appConfig,
|
||||
$this->logger
|
||||
);
|
||||
}
|
||||
|
||||
public function testNotEnabled(): void {
|
||||
$this->appConfig->expects(self::once())
|
||||
->method('getInactiveLockTime')
|
||||
->willReturn(0);
|
||||
$this->appConfig->expects(self::once())
|
||||
->method('enableLobbyOnLockedRooms')
|
||||
->willReturn(false);
|
||||
$this->timeFactory->expects(self::never())
|
||||
->method(self::anything());
|
||||
$this->roomService->expects(self::never())
|
||||
->method(self::anything());
|
||||
$this->logger->expects(self::never())
|
||||
->method(self::anything());
|
||||
|
||||
$this->job->run('t');
|
||||
}
|
||||
|
||||
public function testNoRooms(): void {
|
||||
$this->appConfig->expects(self::once())
|
||||
->method('getInactiveLockTime')
|
||||
->willReturn(123);
|
||||
$this->appConfig->expects(self::once())
|
||||
->method('enableLobbyOnLockedRooms')
|
||||
->willReturn(false);
|
||||
$this->timeFactory->expects(self::once())
|
||||
->method('getTime');
|
||||
$this->timeFactory->expects(self::once())
|
||||
->method('getDateTime');
|
||||
$this->roomService->expects(self::once())
|
||||
->method('getInactiveRooms')
|
||||
->willReturn([]);
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setReadOnly');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setLobby');
|
||||
$this->logger->expects(self::never())
|
||||
->method(self::anything());
|
||||
|
||||
$this->job->run('t');
|
||||
|
||||
}
|
||||
|
||||
public function testLockRooms(): void {
|
||||
$rooms = [
|
||||
$this->createConfiguredMock(Room::class, [
|
||||
'getReadOnly' => 0,
|
||||
'getType' => Room::TYPE_PUBLIC,
|
||||
]),
|
||||
$this->createConfiguredMock(Room::class, [
|
||||
'getReadOnly' => 0,
|
||||
'getType' => Room::TYPE_GROUP,
|
||||
]),
|
||||
];
|
||||
|
||||
$this->appConfig->expects(self::once())
|
||||
->method('getInactiveLockTime')
|
||||
->willReturn(123);
|
||||
$this->appConfig->expects(self::once())
|
||||
->method('enableLobbyOnLockedRooms')
|
||||
->willReturn(false);
|
||||
$this->timeFactory->expects(self::once())
|
||||
->method('getTime');
|
||||
$this->timeFactory->expects(self::once())
|
||||
->method('getDateTime');
|
||||
$this->roomService->expects(self::once())
|
||||
->method('getInactiveRooms')
|
||||
->willReturn($rooms);
|
||||
$this->roomService->expects(self::exactly(2))
|
||||
->method('setReadOnly');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setLobby');
|
||||
$this->logger->expects(self::exactly(2))
|
||||
->method('debug');
|
||||
|
||||
$this->job->run('t');
|
||||
|
||||
}
|
||||
|
||||
public function testLockRoomsAndEnableLobby(): void {
|
||||
$rooms = [
|
||||
$this->createConfiguredMock(Room::class, [
|
||||
'getReadOnly' => 0,
|
||||
'getType' => Room::TYPE_PUBLIC,
|
||||
]),
|
||||
$this->createConfiguredMock(Room::class, [
|
||||
'getReadOnly' => 0,
|
||||
'getType' => Room::TYPE_GROUP,
|
||||
]),
|
||||
];
|
||||
|
||||
$this->appConfig->expects(self::once())
|
||||
->method('getInactiveLockTime')
|
||||
->willReturn(123);
|
||||
$this->appConfig->expects(self::once())
|
||||
->method('enableLobbyOnLockedRooms')
|
||||
->willReturn(true);
|
||||
$this->timeFactory->expects(self::once())
|
||||
->method('getTime');
|
||||
$this->timeFactory->expects(self::any())
|
||||
->method('getDateTime');
|
||||
$this->roomService->expects(self::once())
|
||||
->method('getInactiveRooms')
|
||||
->willReturn($rooms);
|
||||
$this->roomService->expects(self::exactly(2))
|
||||
->method('setReadOnly');
|
||||
$this->roomService->expects(self::exactly(2))
|
||||
->method('setLobby');
|
||||
$this->logger->expects(self::exactly(4))
|
||||
->method('debug');
|
||||
|
||||
$this->job->run('t');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\BackgroundJob;
|
||||
|
||||
use OCA\Talk\BackgroundJob\RemoveEmptyRooms;
|
||||
use OCA\Talk\Federation\FederationManager;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\RoomService;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Files\Config\IUserMountCache;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class RemoveEmptyRoomsTest extends TestCase {
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected Manager&MockObject $manager;
|
||||
protected RoomService&MockObject $roomService;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected FederationManager&MockObject $federationManager;
|
||||
protected LoggerInterface&MockObject $loggerInterface;
|
||||
protected IUserMountCache&MockObject $userMountCache;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->manager = $this->createMock(Manager::class);
|
||||
$this->roomService = $this->createMock(RoomService::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->federationManager = $this->createMock(FederationManager::class);
|
||||
$this->loggerInterface = $this->createMock(LoggerInterface::class);
|
||||
$this->userMountCache = $this->createMock(IUserMountCache::class);
|
||||
}
|
||||
|
||||
public function getBackgroundJob(): RemoveEmptyRooms {
|
||||
return new RemoveEmptyRooms(
|
||||
$this->timeFactory,
|
||||
$this->manager,
|
||||
$this->roomService,
|
||||
$this->participantService,
|
||||
$this->federationManager,
|
||||
$this->loggerInterface,
|
||||
$this->userMountCache,
|
||||
);
|
||||
}
|
||||
|
||||
public function testDoDeleteRoom(): void {
|
||||
$backgroundJob = $this->getBackgroundJob();
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getType')
|
||||
->willReturn(Room::TYPE_GROUP);
|
||||
$numDeletedRooms = self::invokePrivate($backgroundJob, 'numDeletedRooms');
|
||||
$this->assertEquals(0, $numDeletedRooms, 'Invalid default quantity of rooms');
|
||||
|
||||
self::invokePrivate($backgroundJob, 'doDeleteRoom', [$room]);
|
||||
|
||||
$numDeletedRooms = self::invokePrivate($backgroundJob, 'numDeletedRooms');
|
||||
$this->assertEquals(1, $numDeletedRooms, 'Invalid final quantity of rooms');
|
||||
}
|
||||
|
||||
public static function dataDeleteIfFileIsRemoved(): array {
|
||||
return [
|
||||
['', [], 0],
|
||||
['email', [], 0],
|
||||
['file', ['fileExists'], 0],
|
||||
['file', [], 1],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataDeleteIfFileIsRemoved')]
|
||||
public function testDeleteIfFileIsRemoved(string $objectType, array $fileList, int $numDeletedRoomsExpected): void {
|
||||
$backgroundJob = $this->getBackgroundJob();
|
||||
|
||||
$numDeletedRoomsActual = self::invokePrivate($backgroundJob, 'numDeletedRooms');
|
||||
$this->assertEquals(0, $numDeletedRoomsActual, 'Invalid default quantity of rooms');
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getType')
|
||||
->willReturn(Room::TYPE_GROUP);
|
||||
$room->method('getObjectType')
|
||||
->willReturn($objectType);
|
||||
|
||||
$userMountCache = self::invokePrivate($backgroundJob, 'userMountCache');
|
||||
$userMountCache->method('getMountsForFileId')
|
||||
->willReturn($fileList);
|
||||
|
||||
self::invokePrivate($backgroundJob, 'deleteIfFileIsRemoved', [$room]);
|
||||
|
||||
$numDeletedRoomsActual = self::invokePrivate($backgroundJob, 'numDeletedRooms');
|
||||
$this->assertEquals($numDeletedRoomsExpected, $numDeletedRoomsActual, 'Invalid final quantity of rooms');
|
||||
}
|
||||
|
||||
public static function dataDeleteIfIsEmpty(): array {
|
||||
return [
|
||||
'room with user' => ['', 1, 0, 0],
|
||||
'room with fed invite' => ['', 0, 1, 0],
|
||||
'room to delete' => ['', 0, 0, 1],
|
||||
'file room with user' => ['file', 1, 0, 0],
|
||||
'email room with user' => ['email', 1, 0, 0],
|
||||
'email room without user' => ['email', 0, 0, 1]
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataDeleteIfIsEmpty')]
|
||||
public function testDeleteIfIsEmpty(string $objectType, int $actorsCount, int $inviteCount, int $numDeletedRoomsExpected): void {
|
||||
$backgroundJob = $this->getBackgroundJob();
|
||||
|
||||
$numDeletedRoomsActual = self::invokePrivate($backgroundJob, 'numDeletedRooms');
|
||||
$this->assertEquals(0, $numDeletedRoomsActual, 'Invalid default quantity of rooms');
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getType')
|
||||
->willReturn(Room::TYPE_GROUP);
|
||||
$room->method('getObjectType')
|
||||
->willReturn($objectType);
|
||||
$room->method('isFederatedConversation')
|
||||
->willReturn($inviteCount > 0);
|
||||
|
||||
$this->federationManager->method('getNumberOfInvitations')
|
||||
->with($room)
|
||||
->willReturn($inviteCount);
|
||||
|
||||
$participantService = self::invokePrivate($backgroundJob, 'participantService');
|
||||
$participantService->method('getNumberOfActors')
|
||||
->willReturn($actorsCount);
|
||||
|
||||
self::invokePrivate($backgroundJob, 'deleteIfIsEmpty', [$room]);
|
||||
|
||||
$numDeletedRoomsActual = self::invokePrivate($backgroundJob, 'numDeletedRooms');
|
||||
$this->assertEquals($numDeletedRoomsExpected, $numDeletedRoomsActual, 'Invalid final quantity of rooms');
|
||||
}
|
||||
|
||||
#[DataProvider('dataCallback')]
|
||||
public function testCallback(int $roomType, string $objectType, int $numDeletedRoomsExpected): void {
|
||||
$backgroundJob = $this->getBackgroundJob();
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getType')
|
||||
->willReturn($roomType);
|
||||
$room->method('getObjectType')
|
||||
->willReturn($objectType);
|
||||
$backgroundJob->callback($room);
|
||||
$numDeletedRoomsActual = self::invokePrivate($backgroundJob, 'numDeletedRooms');
|
||||
$this->assertEquals($numDeletedRoomsExpected, $numDeletedRoomsActual, 'Invalid final quantity of rooms');
|
||||
}
|
||||
|
||||
public static function dataCallback(): array {
|
||||
return [
|
||||
[Room::TYPE_CHANGELOG, '', 0],
|
||||
[Room::TYPE_GROUP, 'file', 1],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,570 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\Unit;
|
||||
|
||||
use OCA\Talk\Capabilities;
|
||||
use OCA\Talk\Chat\CommentsManager;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\LiveTranscriptionService;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Services\IAppConfig;
|
||||
use OCP\Capabilities\IPublicCapability;
|
||||
use OCP\ICache;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserSession;
|
||||
use OCP\TaskProcessing\IManager as ITaskProcessingManager;
|
||||
use OCP\TaskProcessing\TaskTypes\TextToTextFormalization;
|
||||
use OCP\TaskProcessing\TaskTypes\TextToTextSummary;
|
||||
use OCP\TaskProcessing\TaskTypes\TextToTextTranslate;
|
||||
use OCP\Translation\ITranslationManager;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class CapabilitiesTest extends TestCase {
|
||||
protected IConfig&MockObject $serverConfig;
|
||||
protected Config&MockObject $talkConfig;
|
||||
protected IAppConfig&MockObject $appConfig;
|
||||
protected CommentsManager&MockObject $commentsManager;
|
||||
protected IUserSession&MockObject $userSession;
|
||||
protected IAppManager&MockObject $appManager;
|
||||
protected ITranslationManager&MockObject $translationManager;
|
||||
protected ITaskProcessingManager&MockObject $taskProcessingManager;
|
||||
protected LiveTranscriptionService&MockObject $liveTranscriptionService;
|
||||
protected ICacheFactory&MockObject $cacheFactory;
|
||||
protected ICache&MockObject $talkCache;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->serverConfig = $this->createMock(IConfig::class);
|
||||
$this->talkConfig = $this->createMock(Config::class);
|
||||
$this->appConfig = $this->createMock(IAppConfig::class);
|
||||
$this->commentsManager = $this->createMock(CommentsManager::class);
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->appManager = $this->createMock(IAppManager::class);
|
||||
$this->translationManager = $this->createMock(ITranslationManager::class);
|
||||
$this->taskProcessingManager = $this->createMock(ITaskProcessingManager::class);
|
||||
$this->liveTranscriptionService = $this->createMock(LiveTranscriptionService::class);
|
||||
$this->cacheFactory = $this->createMock(ICacheFactory::class);
|
||||
$this->talkCache = $this->createMock(ICache::class);
|
||||
|
||||
$this->cacheFactory->method('createLocal')
|
||||
->with('talk::')
|
||||
->willReturn($this->talkCache);
|
||||
|
||||
$this->commentsManager->expects($this->any())
|
||||
->method('supportReactions')
|
||||
->willReturn(true);
|
||||
|
||||
$this->appManager->expects($this->any())
|
||||
->method('getAppVersion')
|
||||
->with('spreed')
|
||||
->willReturn('1.2.3');
|
||||
}
|
||||
|
||||
protected function getCapabilities(): Capabilities {
|
||||
return new Capabilities(
|
||||
$this->serverConfig,
|
||||
$this->talkConfig,
|
||||
$this->appConfig,
|
||||
$this->commentsManager,
|
||||
$this->userSession,
|
||||
$this->appManager,
|
||||
$this->translationManager,
|
||||
$this->taskProcessingManager,
|
||||
$this->liveTranscriptionService,
|
||||
$this->cacheFactory,
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetCapabilitiesGuest(): void {
|
||||
$capabilities = $this->getCapabilities();
|
||||
|
||||
$this->userSession->expects($this->once())
|
||||
->method('getUser')
|
||||
->willReturn(null);
|
||||
|
||||
$this->talkConfig->expects($this->never())
|
||||
->method('isDisabledForUser');
|
||||
|
||||
$this->talkConfig->method('getConversationsListStyle')
|
||||
->willReturn('two-lines');
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('isBreakoutRoomsEnabled')
|
||||
->willReturn(false);
|
||||
|
||||
$this->serverConfig->expects($this->any())
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['spreed', 'max-gif-size', '3145728', '200000'],
|
||||
['spreed', 'start_calls', (string)Room::START_CALL_EVERYONE, (string)Room::START_CALL_EVERYONE],
|
||||
['spreed', 'session-ping-limit', '200', '200'],
|
||||
['core', 'backgroundjobs_mode', 'ajax', 'cron'],
|
||||
]);
|
||||
|
||||
$this->appConfig->method('getAppValueInt')
|
||||
->willReturnMap([
|
||||
['max_call_duration', 0, 0],
|
||||
['retention_event_rooms', 28, 28],
|
||||
['retention_phone_rooms', 7, 7],
|
||||
['retention_instant_meetings', 1, 1],
|
||||
['experiments_guests', 0, 0],
|
||||
['summary_threshold', 100, 100],
|
||||
]);
|
||||
|
||||
$this->talkConfig->expects($this->any())
|
||||
->method('getSignalingMode')
|
||||
->willReturn('internal');
|
||||
|
||||
$this->talkConfig->expects($this->any())
|
||||
->method('getDefaultPermissions')
|
||||
->willReturn(246);
|
||||
|
||||
$this->assertInstanceOf(IPublicCapability::class, $capabilities);
|
||||
$this->assertSame([
|
||||
'spreed' => [
|
||||
'features' => array_merge(
|
||||
Capabilities::FEATURES, [
|
||||
'message-expiration',
|
||||
'reactions',
|
||||
]
|
||||
),
|
||||
'features-local' => Capabilities::LOCAL_FEATURES,
|
||||
'config' => [
|
||||
'attachments' => [
|
||||
'allowed' => false,
|
||||
],
|
||||
'call' => [
|
||||
'enabled' => true,
|
||||
'breakout-rooms' => false,
|
||||
'recording' => false,
|
||||
'recording-consent' => 0,
|
||||
'supported-reactions' => ['❤️', '🎉', '👏', '👋', '👍', '👎', '🔥', '😂', '🤩', '🤔', '😲', '😥'],
|
||||
'can-upload-background' => false,
|
||||
'sip-enabled' => false,
|
||||
'sip-dialout-enabled' => false,
|
||||
'default-phone-region' => '',
|
||||
'can-enable-sip' => false,
|
||||
'start-without-media' => false,
|
||||
'max-duration' => 0,
|
||||
'blur-virtual-background' => false,
|
||||
'end-to-end-encryption' => false,
|
||||
'live-transcription' => false,
|
||||
'play-sounds' => false,
|
||||
'grid-limit' => 0,
|
||||
'grid-limit-enforced' => false,
|
||||
'predefined-backgrounds' => [
|
||||
'1_office.jpg',
|
||||
'2_home.jpg',
|
||||
'3_abstract.jpg',
|
||||
'4_beach.jpg',
|
||||
'5_park.jpg',
|
||||
'6_theater.jpg',
|
||||
'7_library.jpg',
|
||||
'8_space_station.jpg',
|
||||
],
|
||||
'predefined-backgrounds-v2' => [
|
||||
'/img/backgrounds/1_office.jpg',
|
||||
'/img/backgrounds/2_home.jpg',
|
||||
'/img/backgrounds/3_abstract.jpg',
|
||||
'/img/backgrounds/4_beach.jpg',
|
||||
'/img/backgrounds/5_park.jpg',
|
||||
'/img/backgrounds/6_theater.jpg',
|
||||
'/img/backgrounds/7_library.jpg',
|
||||
'/img/backgrounds/8_space_station.jpg',
|
||||
],
|
||||
],
|
||||
'chat' => [
|
||||
'max-length' => 32000,
|
||||
'read-privacy' => 0,
|
||||
'has-translation-providers' => false,
|
||||
'has-translation-task-providers' => false,
|
||||
'typing-privacy' => 0,
|
||||
'summary-threshold' => 100,
|
||||
'matterbridge-enabled' => false,
|
||||
],
|
||||
'conversations' => [
|
||||
'can-create' => false,
|
||||
'force-passwords' => false,
|
||||
'list-style' => 'two-lines',
|
||||
'description-length' => 2000,
|
||||
'retention-event' => 28,
|
||||
'retention-phone' => 7,
|
||||
'retention-instant-meetings' => 1,
|
||||
],
|
||||
'federation' => [
|
||||
'enabled' => false,
|
||||
'incoming-enabled' => false,
|
||||
'outgoing-enabled' => false,
|
||||
'only-trusted-servers' => true,
|
||||
],
|
||||
'previews' => [
|
||||
'max-gif-size' => 200000,
|
||||
],
|
||||
'signaling' => [
|
||||
'session-ping-limit' => 200,
|
||||
'mode' => 'internal',
|
||||
],
|
||||
'experiments' => [
|
||||
'enabled' => 0,
|
||||
],
|
||||
'permissions' => [
|
||||
'max-default' => 254,
|
||||
'max-custom' => 255,
|
||||
'default' => 246,
|
||||
],
|
||||
],
|
||||
'config-local' => Capabilities::LOCAL_CONFIGS,
|
||||
'version' => '1.2.3',
|
||||
],
|
||||
], $capabilities->getCapabilities());
|
||||
}
|
||||
|
||||
public static function dataGetCapabilitiesUserAllowed(): array {
|
||||
return [
|
||||
[true, false, 'none', true, Participant::PRIVACY_PRIVATE],
|
||||
[false, true, '1 MB', true, Participant::PRIVACY_PUBLIC],
|
||||
[false, true, '0 B', false, Participant::PRIVACY_PUBLIC],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataGetCapabilitiesUserAllowed')]
|
||||
public function testGetCapabilitiesUserAllowed(bool $isNotAllowed, bool $canCreate, string $quota, bool $canUpload, int $readPrivacy): void {
|
||||
$capabilities = $this->getCapabilities();
|
||||
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->expects($this->atLeastOnce())
|
||||
->method('getUID')
|
||||
->willReturn('uid');
|
||||
$this->userSession->expects($this->once())
|
||||
->method('getUser')
|
||||
->willReturn($user);
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('isDisabledForUser')
|
||||
->with($user)
|
||||
->willReturn(false);
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('isBreakoutRoomsEnabled')
|
||||
->willReturn(true);
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('getAttachmentFolder')
|
||||
->with('uid')
|
||||
->willReturn('/Talk');
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('isNotAllowedToCreateConversations')
|
||||
->with($user)
|
||||
->willReturn($isNotAllowed);
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('getUserReadPrivacy')
|
||||
->with('uid')
|
||||
->willReturn($readPrivacy);
|
||||
|
||||
$this->talkConfig->method('getConversationsListStyle')
|
||||
->willReturn('two-lines');
|
||||
|
||||
$user->method('getQuota')
|
||||
->willReturn($quota);
|
||||
|
||||
$this->taskProcessingManager->method('getAvailableTaskTypeIds')
|
||||
->willReturn([TextToTextSummary::ID]);
|
||||
|
||||
$this->serverConfig->expects($this->any())
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
['spreed', 'max-gif-size', '3145728', '200000'],
|
||||
['spreed', 'start_calls', (string)Room::START_CALL_EVERYONE, (string)Room::START_CALL_NOONE],
|
||||
['spreed', 'session-ping-limit', '200', '50'],
|
||||
['core', 'backgroundjobs_mode', 'ajax', 'cron'],
|
||||
]);
|
||||
|
||||
$this->appConfig->expects($this->any())
|
||||
->method('getAppValueBool')
|
||||
->willReturnMap([
|
||||
['backgrounds_default_for_users', true, true],
|
||||
['backgrounds_upload_users', true, true],
|
||||
]);
|
||||
|
||||
$this->appConfig->method('getAppValueInt')
|
||||
->willReturnMap([
|
||||
['max_call_duration', 0, 0],
|
||||
['retention_event_rooms', 28, 28],
|
||||
['retention_phone_rooms', 7, 7],
|
||||
['retention_instant_meetings', 1, 1],
|
||||
['experiments_users', 0, 0],
|
||||
['summary_threshold', 100, 100],
|
||||
]);
|
||||
|
||||
$this->serverConfig->expects($this->any())
|
||||
->method('getSystemValueString')
|
||||
->willReturnMap([
|
||||
['default_phone_region', '', 'DE'],
|
||||
]);
|
||||
|
||||
$this->talkConfig->expects($this->any())
|
||||
->method('getSignalingMode')
|
||||
->willReturn('internal');
|
||||
|
||||
$this->talkConfig->expects($this->any())
|
||||
->method('getDefaultPermissions')
|
||||
->willReturn(246);
|
||||
|
||||
$this->assertInstanceOf(IPublicCapability::class, $capabilities);
|
||||
$data = $capabilities->getCapabilities();
|
||||
$this->assertSame([
|
||||
'spreed' => [
|
||||
'features' => array_merge(
|
||||
Capabilities::FEATURES, [
|
||||
'message-expiration',
|
||||
'reactions',
|
||||
'chat-summary-api',
|
||||
]
|
||||
),
|
||||
'features-local' => Capabilities::LOCAL_FEATURES,
|
||||
'config' => [
|
||||
'attachments' => [
|
||||
'allowed' => true,
|
||||
'folder' => '/Talk',
|
||||
],
|
||||
'call' => [
|
||||
'enabled' => false,
|
||||
'breakout-rooms' => true,
|
||||
'recording' => false,
|
||||
'recording-consent' => 0,
|
||||
'supported-reactions' => ['❤️', '🎉', '👏', '👋', '👍', '👎', '🔥', '😂', '🤩', '🤔', '😲', '😥'],
|
||||
'can-upload-background' => $canUpload,
|
||||
'sip-enabled' => false,
|
||||
'sip-dialout-enabled' => false,
|
||||
'default-phone-region' => 'DE',
|
||||
'can-enable-sip' => false,
|
||||
'start-without-media' => false,
|
||||
'max-duration' => 0,
|
||||
'blur-virtual-background' => false,
|
||||
'end-to-end-encryption' => false,
|
||||
'live-transcription' => false,
|
||||
'play-sounds' => false,
|
||||
'grid-limit' => 0,
|
||||
'grid-limit-enforced' => false,
|
||||
'predefined-backgrounds' => [
|
||||
'1_office.jpg',
|
||||
'2_home.jpg',
|
||||
'3_abstract.jpg',
|
||||
'4_beach.jpg',
|
||||
'5_park.jpg',
|
||||
'6_theater.jpg',
|
||||
'7_library.jpg',
|
||||
'8_space_station.jpg',
|
||||
],
|
||||
'predefined-backgrounds-v2' => [
|
||||
'/img/backgrounds/1_office.jpg',
|
||||
'/img/backgrounds/2_home.jpg',
|
||||
'/img/backgrounds/3_abstract.jpg',
|
||||
'/img/backgrounds/4_beach.jpg',
|
||||
'/img/backgrounds/5_park.jpg',
|
||||
'/img/backgrounds/6_theater.jpg',
|
||||
'/img/backgrounds/7_library.jpg',
|
||||
'/img/backgrounds/8_space_station.jpg',
|
||||
],
|
||||
],
|
||||
'chat' => [
|
||||
'max-length' => 32000,
|
||||
'read-privacy' => $readPrivacy,
|
||||
'has-translation-providers' => false,
|
||||
'has-translation-task-providers' => false,
|
||||
'typing-privacy' => 0,
|
||||
'summary-threshold' => 100,
|
||||
'matterbridge-enabled' => false,
|
||||
],
|
||||
'conversations' => [
|
||||
'can-create' => $canCreate,
|
||||
'force-passwords' => false,
|
||||
'list-style' => 'two-lines',
|
||||
'description-length' => 2000,
|
||||
'retention-event' => 28,
|
||||
'retention-phone' => 7,
|
||||
'retention-instant-meetings' => 1,
|
||||
],
|
||||
'federation' => [
|
||||
'enabled' => false,
|
||||
'incoming-enabled' => false,
|
||||
'outgoing-enabled' => false,
|
||||
'only-trusted-servers' => true,
|
||||
],
|
||||
'previews' => [
|
||||
'max-gif-size' => 200000,
|
||||
],
|
||||
'signaling' => [
|
||||
'session-ping-limit' => 50,
|
||||
'mode' => 'internal',
|
||||
],
|
||||
'experiments' => [
|
||||
'enabled' => 0,
|
||||
],
|
||||
'permissions' => [
|
||||
'max-default' => 254,
|
||||
'max-custom' => 255,
|
||||
'default' => 246,
|
||||
],
|
||||
],
|
||||
'config-local' => Capabilities::LOCAL_CONFIGS,
|
||||
'version' => '1.2.3',
|
||||
],
|
||||
], $data);
|
||||
}
|
||||
|
||||
public function testCapabilitiesDocumentation(): void {
|
||||
foreach (Capabilities::FEATURES as $feature) {
|
||||
$suffix = ' - ';
|
||||
if (in_array($feature, Capabilities::LOCAL_FEATURES)) {
|
||||
$suffix = ' (local) - ';
|
||||
}
|
||||
$this->assertCapabilityIsDocumented("`$feature`" . $suffix);
|
||||
}
|
||||
|
||||
foreach (Capabilities::CONDITIONAL_FEATURES as $feature) {
|
||||
$suffix = ' - ';
|
||||
if (in_array($feature, Capabilities::LOCAL_FEATURES)) {
|
||||
$suffix = ' (local) - ';
|
||||
}
|
||||
$this->assertCapabilityIsDocumented("`$feature`" . $suffix);
|
||||
}
|
||||
|
||||
$openapi = json_decode(file_get_contents(__DIR__ . '/../../openapi.json'), true, flags: JSON_THROW_ON_ERROR);
|
||||
$configDefinition = $openapi['components']['schemas']['Capabilities']['properties']['config']['properties'] ?? null;
|
||||
$this->assertIsArray($configDefinition, 'Failed to read Capabilities config from openapi.json');
|
||||
|
||||
$configFeatures = array_keys($configDefinition);
|
||||
|
||||
foreach ($configFeatures as $feature) {
|
||||
foreach (array_keys($configDefinition[$feature]['properties']) as $config) {
|
||||
$suffix = '';
|
||||
if (in_array($config, Capabilities::LOCAL_CONFIGS[$feature])) {
|
||||
$suffix = ' (local)';
|
||||
}
|
||||
$this->assertCapabilityIsDocumented("`config => $feature => $config`" . $suffix . ' - ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function assertCapabilityIsDocumented(string $capability): void {
|
||||
$docs = file_get_contents(__DIR__ . '/../../docs/capabilities.md');
|
||||
self::assertStringContainsString($capability, $docs, 'Asserting that capability ' . $capability . ' is documented');
|
||||
}
|
||||
|
||||
public function testGetCapabilitiesUserDisallowed(): void {
|
||||
$capabilities = $this->getCapabilities();
|
||||
|
||||
$user = $this->createMock(IUser::class);
|
||||
$this->userSession->expects($this->once())
|
||||
->method('getUser')
|
||||
->willReturn($user);
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('isDisabledForUser')
|
||||
->with($user)
|
||||
->willReturn(true);
|
||||
|
||||
$this->assertInstanceOf(IPublicCapability::class, $capabilities);
|
||||
$this->assertSame([], $capabilities->getCapabilities());
|
||||
}
|
||||
|
||||
public function testCapabilitiesHelloV2Key(): void {
|
||||
$capabilities = $this->getCapabilities();
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('getSignalingTokenPublicKey')
|
||||
->willReturn('this-is-the-key');
|
||||
|
||||
$data = $capabilities->getCapabilities();
|
||||
$this->assertEquals('this-is-the-key', $data['spreed']['config']['signaling']['hello-v2-token-key']);
|
||||
}
|
||||
|
||||
public static function dataTestConfigRecording(): array {
|
||||
return [
|
||||
[true],
|
||||
[false],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataTestConfigRecording')]
|
||||
public function testConfigRecording(bool $enabled): void {
|
||||
$capabilities = $this->getCapabilities();
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('isRecordingEnabled')
|
||||
->willReturn($enabled);
|
||||
|
||||
$data = $capabilities->getCapabilities();
|
||||
$this->assertEquals($data['spreed']['config']['call']['recording'], $enabled);
|
||||
}
|
||||
|
||||
public static function dataTestConfigCallLiveTranscription(): array {
|
||||
return [
|
||||
[Config::SIGNALING_EXTERNAL, true, true],
|
||||
[Config::SIGNALING_EXTERNAL, false, false],
|
||||
[Config::SIGNALING_INTERNAL, true, false],
|
||||
[Config::SIGNALING_INTERNAL, false, false],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataTestConfigCallLiveTranscription')]
|
||||
public function testConfigCallLiveTranscription(string $signalingMode, bool $liveTranscriptionAppEnabled, bool $expectedEnabled): void {
|
||||
$capabilities = $this->getCapabilities();
|
||||
|
||||
$this->talkConfig->expects($this->any())
|
||||
->method('getSignalingMode')
|
||||
->willReturn($signalingMode);
|
||||
|
||||
$this->liveTranscriptionService->expects($this->any())
|
||||
->method('isLiveTranscriptionAppEnabled')
|
||||
->willReturn($liveTranscriptionAppEnabled);
|
||||
|
||||
$data = $capabilities->getCapabilities();
|
||||
$this->assertEquals($data['spreed']['config']['call']['live-transcription'], $expectedEnabled);
|
||||
}
|
||||
|
||||
public function testCapabilitiesTranslations(): void {
|
||||
$capabilities = $this->getCapabilities();
|
||||
|
||||
$this->translationManager->method('hasProviders')
|
||||
->willReturn(true);
|
||||
|
||||
$data = json_decode(json_encode($capabilities->getCapabilities(), JSON_THROW_ON_ERROR), true);
|
||||
$this->assertEquals(true, $data['spreed']['config']['chat']['has-translation-providers']);
|
||||
}
|
||||
|
||||
public function testCapabilitiesTranslationsTaskProviders(): void {
|
||||
$capabilities = $this->getCapabilities();
|
||||
|
||||
$this->taskProcessingManager->method('getAvailableTaskTypeIds')
|
||||
->willReturn([TextToTextTranslate::ID]);
|
||||
|
||||
$data = json_decode(json_encode($capabilities->getCapabilities(), JSON_THROW_ON_ERROR), true);
|
||||
$this->assertEquals(true, $data['spreed']['config']['chat']['has-translation-task-providers']);
|
||||
}
|
||||
|
||||
public function testSummaryTaskProviders(): void {
|
||||
$capabilities = $this->getCapabilities();
|
||||
|
||||
$this->taskProcessingManager->method('getAvailableTaskTypeIds')
|
||||
->willReturn([TextToTextFormalization::ID]);
|
||||
|
||||
$data = json_decode(json_encode($capabilities->getCapabilities(), JSON_THROW_ON_ERROR), true);
|
||||
$this->assertNotContains('chat-summary-api', $data['spreed']['features']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Chat\AutoComplete;
|
||||
|
||||
use OC\Collaboration\Collaborators\SearchResult;
|
||||
use OCA\Talk\Chat\AutoComplete\SearchPlugin;
|
||||
use OCA\Talk\Federation\Authenticator;
|
||||
use OCA\Talk\Files\Util;
|
||||
use OCA\Talk\GuestManager;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Model\Session;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\TalkSession;
|
||||
use OCP\Collaboration\Collaborators\ISearchResult;
|
||||
use OCP\IL10N;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class SearchPluginTest extends TestCase {
|
||||
protected IUserManager&MockObject $userManager;
|
||||
protected GuestManager&MockObject $guestManager;
|
||||
protected TalkSession&MockObject $talkSession;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected Util&MockObject $util;
|
||||
protected Authenticator&MockObject $federationAuthenticator;
|
||||
protected IL10N&MockObject $l;
|
||||
protected ?string $userId = null;
|
||||
protected SearchPlugin $plugin;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->guestManager = $this->createMock(GuestManager::class);
|
||||
$this->talkSession = $this->createMock(TalkSession::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->util = $this->createMock(Util::class);
|
||||
$this->federationAuthenticator = $this->createMock(Authenticator::class);
|
||||
$this->userId = 'current';
|
||||
$this->l = $this->createMock(IL10N::class);
|
||||
$this->l->expects($this->any())
|
||||
->method('t')
|
||||
->willReturnCallback(function ($text, $parameters = []) {
|
||||
return vsprintf($text, $parameters);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $methods
|
||||
* @return SearchPlugin|MockObject
|
||||
*/
|
||||
protected function getPlugin(array $methods = []) {
|
||||
if (empty($methods)) {
|
||||
return new SearchPlugin(
|
||||
$this->userManager,
|
||||
$this->guestManager,
|
||||
$this->talkSession,
|
||||
$this->participantService,
|
||||
$this->util,
|
||||
$this->userId,
|
||||
$this->l,
|
||||
$this->federationAuthenticator,
|
||||
);
|
||||
}
|
||||
|
||||
return $this->getMockBuilder(SearchPlugin::class)
|
||||
->setConstructorArgs([
|
||||
$this->userManager,
|
||||
$this->guestManager,
|
||||
$this->talkSession,
|
||||
$this->participantService,
|
||||
$this->util,
|
||||
$this->userId,
|
||||
$this->l,
|
||||
$this->federationAuthenticator,
|
||||
])
|
||||
->onlyMethods($methods)
|
||||
->getMock();
|
||||
}
|
||||
|
||||
protected function createParticipantMock(string $uid, string $displayName, string $session = ''): Participant {
|
||||
/** @var Participant&MockObject $p */
|
||||
$p = $this->createMock(Participant::class);
|
||||
$a = Attendee::fromRow([
|
||||
'actor_type' => $uid ? 'users' : 'guests',
|
||||
'actor_id' => $uid ?: sha1($session),
|
||||
'display_name' => $displayName,
|
||||
]);
|
||||
$s = Session::fromRow([
|
||||
'session_id' => $session,
|
||||
]);
|
||||
$p->expects($this->any())
|
||||
->method('getAttendee')
|
||||
->willReturn($a);
|
||||
$p->expects($this->any())
|
||||
->method('getSession')
|
||||
->willReturn($s);
|
||||
|
||||
$p->expects($this->any())
|
||||
->method('isGuest')
|
||||
->willReturn($uid === '');
|
||||
|
||||
return $p;
|
||||
}
|
||||
|
||||
public function testSearch(): void {
|
||||
$result = $this->createMock(ISearchResult::class);
|
||||
$room = $this->createMock(Room::class);
|
||||
|
||||
$this->participantService->expects($this->once())
|
||||
->method('getParticipantsForRoom')
|
||||
->with($room)
|
||||
->willReturn([
|
||||
$this->createParticipantMock('123', 'OneTwoThree'),
|
||||
$this->createParticipantMock('foo', 'Foo Bar'),
|
||||
$this->createParticipantMock('', 'Guest 1-6', '123456'),
|
||||
$this->createParticipantMock('bar', 'Bar Tender'),
|
||||
$this->createParticipantMock('', 'Guest a-f', 'abcdef'),
|
||||
]);
|
||||
|
||||
$plugin = $this->getPlugin(['searchUsers', 'searchGuests']);
|
||||
$plugin->setContext(['room' => $room]);
|
||||
$plugin->expects($this->once())
|
||||
->method('searchUsers')
|
||||
->with('fo', ['123' => 'OneTwoThree', 'foo' => 'Foo Bar', 'bar' => 'Bar Tender'], $result)
|
||||
->willReturnCallback(function ($search, $users, $result): void {
|
||||
array_map(function ($user): void {
|
||||
$this->assertIsString($user);
|
||||
}, $users);
|
||||
});
|
||||
$plugin->expects($this->once())
|
||||
->method('searchGuests')
|
||||
->with('fo', $this->anything(), $result)
|
||||
->willReturnCallback(function ($search, $guests, $result): void {
|
||||
array_map(function ($guest): void {
|
||||
$this->assertInstanceOf(Attendee::class, $guest);
|
||||
}, $guests);
|
||||
});
|
||||
|
||||
$plugin->search('fo', 10, 0, $result);
|
||||
}
|
||||
|
||||
public static function dataSearchUsers(): array {
|
||||
return [
|
||||
['test', [], [], [], []],
|
||||
['test', [
|
||||
'current' => 'test',
|
||||
'foo' => '',
|
||||
'test' => 'Te st',
|
||||
'test1' => 'Te st 1',
|
||||
], [['test1' => 'Te st 1']], [['current' => 'test'], ['test' => 'Te st']]],
|
||||
['test', [
|
||||
'foo' => 'Test',
|
||||
'bar' => 'test One',
|
||||
], [['bar' => 'test One']], [['foo' => 'Test']]],
|
||||
['', ['foo' => '', 'bar' => ''], [['foo' => ''], ['bar' => '']], []],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $users
|
||||
*/
|
||||
#[DataProvider('dataSearchUsers')]
|
||||
public function testSearchUsers(string $search, array $users, array $expected, array $expectedExact): void {
|
||||
$result = $this->createMock(ISearchResult::class);
|
||||
|
||||
|
||||
$result->expects($this->once())
|
||||
->method('addResultSet')
|
||||
->with($this->anything(), $expected, $expectedExact);
|
||||
|
||||
$plugin = $this->getPlugin(['createResult']);
|
||||
$plugin->method('createResult')
|
||||
->willReturnCallback(function ($type, $uid, $name) {
|
||||
return [$uid => $name];
|
||||
});
|
||||
|
||||
self::invokePrivate($plugin, 'searchUsers', [$search, $users, $result]);
|
||||
}
|
||||
|
||||
public static function dataSearchGuests(): array {
|
||||
return [
|
||||
['test', [], [], []],
|
||||
['', ['abcdef' => ''], [['abcdef' => 'Guest']], []],
|
||||
['Guest', ['abcdef' => ''], [], [['abcdef' => 'Guest']]],
|
||||
['est', ['abcdef' => '', 'foobar' => 'est'], [['abcdef' => 'Guest']], [['foobar' => 'est']]],
|
||||
['Ast', ['abcdef' => '', 'foobar' => 'ast'], [], [['foobar' => 'ast']]],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataSearchGuests')]
|
||||
public function testSearchGuests(string $search, array $guests, array $expected, array $expectedExact): void {
|
||||
$result = $this->createMock(ISearchResult::class);
|
||||
$result->expects($this->once())
|
||||
->method('addResultSet')
|
||||
->with($this->anything(), $expected, $expectedExact);
|
||||
|
||||
$attendees = [];
|
||||
foreach ($guests as $actorId => $displayName) {
|
||||
$attendees[] = Attendee::fromRow([
|
||||
'actorId' => $actorId,
|
||||
'displayName' => $displayName,
|
||||
]);
|
||||
}
|
||||
|
||||
$plugin = $this->getPlugin(['createGuestResult']);
|
||||
$plugin->expects($this->any())
|
||||
->method('createGuestResult')
|
||||
->willReturnCallback(function ($hash, $name) {
|
||||
return [$hash => $name];
|
||||
});
|
||||
|
||||
self::invokePrivate($plugin, 'searchGuests', [$search, $attendees, $result]);
|
||||
}
|
||||
|
||||
protected function createUserMock(array $userData) {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->willReturn($userData['uid']);
|
||||
$user->expects($this->any())
|
||||
->method('getDisplayName')
|
||||
->willReturn($userData['name']);
|
||||
return $user;
|
||||
}
|
||||
|
||||
public static function dataCreateResult(): array {
|
||||
return [
|
||||
['user', 'foo', 'bar', '', ['label' => 'bar', 'value' => ['shareType' => 'user', 'shareWith' => 'foo']]],
|
||||
['user', 'test', 'Test', '', ['label' => 'Test', 'value' => ['shareType' => 'user', 'shareWith' => 'test']]],
|
||||
['user', 'test', '', 'Test', ['label' => 'Test', 'value' => ['shareType' => 'user', 'shareWith' => 'test']]],
|
||||
['user', 'test', '', null, ['label' => 'test', 'value' => ['shareType' => 'user', 'shareWith' => 'test']]],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataCreateResult')]
|
||||
public function testCreateResult(string $type, string $uid, string $name, ?string $managerName, array $expected): void {
|
||||
if ($managerName !== null) {
|
||||
$this->userManager->expects($this->any())
|
||||
->method('getDisplayName')
|
||||
->with($uid)
|
||||
->willReturn($managerName);
|
||||
} else {
|
||||
$this->userManager->expects($this->any())
|
||||
->method('getDisplayName')
|
||||
->with($uid)
|
||||
->willReturn(null);
|
||||
}
|
||||
|
||||
$plugin = $this->getPlugin();
|
||||
$this->assertEquals($expected, self::invokePrivate($plugin, 'createResult', [$type, $uid, $name]));
|
||||
}
|
||||
|
||||
|
||||
public static function dataCreateGuestResult(): array {
|
||||
return [
|
||||
['1234', 'foo', ['label' => 'foo', 'value' => ['shareType' => 'guest', 'shareWith' => 'guest/1234']]],
|
||||
['abcd', 'bar', ['label' => 'bar', 'value' => ['shareType' => 'guest', 'shareWith' => 'guest/abcd']]],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataCreateGuestResult')]
|
||||
public function testCreateGuestResult(string $actorId, string $name, array $expected): void {
|
||||
$plugin = $this->getPlugin();
|
||||
$this->assertEquals($expected, self::invokePrivate($plugin, 'createGuestResult', [$actorId, $name]));
|
||||
}
|
||||
|
||||
public static function dataSearchGroups(): array {
|
||||
return [
|
||||
// $search, $groups, $isGroup, $totalMatches, $totalExactMatches
|
||||
['', ['groupid' => 'group'], true, 1, 0],
|
||||
['groupid', ['groupid' => 'group'], true, 0, 1],
|
||||
['gro', ['groupid' => 'group'], true, 1, 0],
|
||||
['not', ['groupid' => 'group'], false, 0, 0],
|
||||
['name', ['groupid' => 'name'], true, 0, 1],
|
||||
['na', ['groupid' => 'name'], true, 1, 0],
|
||||
['not', ['groupid' => 'group'], true, 0, 0],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataSearchGroups')]
|
||||
public function testSearchGroups(string $search, array $groups, bool $isGroup, int $totalMatches, int $totalExactMatches): void {
|
||||
$plugin = $this->getPlugin(['createGroupResult']);
|
||||
$plugin->expects($this->any())
|
||||
->method('createGroupResult')
|
||||
->willReturnCallback(function ($groupId) {
|
||||
return [
|
||||
'label' => $groupId,
|
||||
'value' => [
|
||||
'shareType' => 'group',
|
||||
'shareWith' => 'group/' . $groupId,
|
||||
],
|
||||
];
|
||||
});
|
||||
$searchResult = new SearchResult();
|
||||
self::invokePrivate($plugin, 'searchGroups', [$search, $groups, $searchResult]);
|
||||
$actual = $searchResult->asArray();
|
||||
$this->assertCount($totalMatches, $actual['groups']);
|
||||
$this->assertCount($totalExactMatches, $actual['exact']['groups']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Chat\AutoComplete;
|
||||
|
||||
use OCA\Talk\Chat\AutoComplete\Sorter;
|
||||
use OCA\Talk\Chat\CommentsManager;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class SorterTest extends TestCase {
|
||||
protected CommentsManager&MockObject $commentsManager;
|
||||
|
||||
protected string $userId;
|
||||
|
||||
protected ?Sorter $sorter = null;
|
||||
|
||||
protected static array $user1 = [
|
||||
'label' => 'Seattle',
|
||||
'value' => [
|
||||
'shareType' => 'user',
|
||||
'shareWith' => 'seattle',
|
||||
],
|
||||
];
|
||||
|
||||
protected static array $user2 = [
|
||||
'label' => 'New York',
|
||||
'value' => [
|
||||
'shareType' => 'user',
|
||||
'shareWith' => 'new_york',
|
||||
],
|
||||
];
|
||||
|
||||
protected static array $user3 = [
|
||||
'label' => 'ttle Sea',
|
||||
'value' => [
|
||||
'shareType' => 'user',
|
||||
'shareWith' => 'ttle_sea',
|
||||
],
|
||||
];
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->commentsManager = $this->createMock(CommentsManager::class);
|
||||
$this->sorter = new Sorter($this->commentsManager);
|
||||
}
|
||||
|
||||
public function testGetId(): void {
|
||||
$this->assertSame('talk_chat_participants', $this->sorter->getId());
|
||||
}
|
||||
|
||||
public static function dataSort(): array {
|
||||
return [
|
||||
'no user posted' => ['', ['users' => [self::$user1, self::$user2]], [], ['users' => [self::$user1, self::$user2]]],
|
||||
'second user posted' => ['', ['users' => [self::$user1, self::$user2]], ['new_york' => new \DateTime('2000-01-01')], ['users' => [self::$user2, self::$user1]]],
|
||||
'second user posted later' => ['', ['users' => [self::$user1, self::$user2]], ['seattle' => new \DateTime('2017-01-01'), 'new_york' => new \DateTime('2018-01-01')], ['users' => [self::$user2, self::$user1]]],
|
||||
'second user posted earlier' => ['', ['users' => [self::$user1, self::$user2]], ['seattle' => new \DateTime('2018-01-01'), 'new_york' => new \DateTime('2017-01-01')], ['users' => [self::$user1, self::$user2]]],
|
||||
'starting match first1' => ['Sea', ['users' => [self::$user1, self::$user3]], [], ['users' => [self::$user1, self::$user3]]],
|
||||
'starting match first2' => ['Sea', ['users' => [self::$user3, self::$user1]], [], ['users' => [self::$user1, self::$user3]]],
|
||||
'no users' => ['', ['groups' => [self::$user1, self::$user2]], [], ['groups' => [self::$user1, self::$user2]]],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataSort')]
|
||||
public function testSort(string $search, array $toSort, array $comments, array $expected): void {
|
||||
$this->commentsManager->expects(isset($toSort['users']) ? $this->once() : $this->never())
|
||||
->method('getLastCommentDateByActor')
|
||||
->with('chat', '23', 'comment', 'users', $this->anything())
|
||||
->willReturn($comments);
|
||||
|
||||
$this->sorter->sort($toSort, [
|
||||
'itemType' => 'chat',
|
||||
'itemId' => '23',
|
||||
'search' => $search,
|
||||
]);
|
||||
$this->assertSame($expected, $toSort);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,854 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Chat;
|
||||
|
||||
use OCA\Talk\Chat\ChatManager;
|
||||
use OCA\Talk\Chat\CommentsManager;
|
||||
use OCA\Talk\Chat\Notifier;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Model\AttendeeMapper;
|
||||
use OCA\Talk\Model\Invitation;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\AttachmentService;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\PollService;
|
||||
use OCA\Talk\Service\RoomService;
|
||||
use OCA\Talk\Service\ThreadService;
|
||||
use OCA\Talk\Share\RoomShareProvider;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Collaboration\Reference\IReferenceManager;
|
||||
use OCP\Comments\IComment;
|
||||
use OCP\Comments\ICommentsManager;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IL10N;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUser;
|
||||
use OCP\Notification\IManager as INotificationManager;
|
||||
use OCP\Security\RateLimiting\ILimiter;
|
||||
use OCP\Share\Exceptions\ShareNotFound;
|
||||
use OCP\Share\IManager;
|
||||
use OCP\Share\IShare;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class ChatManagerTest extends TestCase {
|
||||
protected CommentsManager|ICommentsManager|MockObject $commentsManager;
|
||||
protected IEventDispatcher&MockObject $dispatcher;
|
||||
protected INotificationManager&MockObject $notificationManager;
|
||||
protected IManager&MockObject $shareManager;
|
||||
protected RoomShareProvider&MockObject $shareProvider;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected RoomService&MockObject $roomService;
|
||||
protected PollService&MockObject $pollService;
|
||||
protected ThreadService&MockObject $threadService;
|
||||
protected Notifier&MockObject $notifier;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected AttachmentService&MockObject $attachmentService;
|
||||
protected IReferenceManager&MockObject $referenceManager;
|
||||
protected ILimiter&MockObject $rateLimiter;
|
||||
protected IRequest&MockObject $request;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
protected IL10N&MockObject $l;
|
||||
protected ?ChatManager $chatManager = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->commentsManager = $this->createMock(CommentsManager::class);
|
||||
$this->dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
$this->notificationManager = $this->createMock(INotificationManager::class);
|
||||
$this->shareManager = $this->createMock(IManager::class);
|
||||
$this->shareProvider = $this->createMock(RoomShareProvider::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->roomService = $this->createMock(RoomService::class);
|
||||
$this->pollService = $this->createMock(PollService::class);
|
||||
$this->threadService = $this->createMock(ThreadService::class);
|
||||
$this->notifier = $this->createMock(Notifier::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->attachmentService = $this->createMock(AttachmentService::class);
|
||||
$this->referenceManager = $this->createMock(IReferenceManager::class);
|
||||
$this->rateLimiter = $this->createMock(ILimiter::class);
|
||||
$this->request = $this->createMock(IRequest::class);
|
||||
$this->l = $this->createMock(IL10N::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$this->l->method('n')
|
||||
->willReturnCallback(function (string $singular, string $plural, int $count, array $parameters = []) {
|
||||
$text = $count === 1 ? $singular : $plural;
|
||||
return vsprintf(str_replace('%n', (string)$count, $text), $parameters);
|
||||
});
|
||||
|
||||
$this->chatManager = $this->getManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $methods
|
||||
* @return ChatManager|MockObject
|
||||
*/
|
||||
protected function getManager(array $methods = []): ChatManager {
|
||||
$cacheFactory = $this->createMock(ICacheFactory::class);
|
||||
|
||||
if (!empty($methods)) {
|
||||
return $this->getMockBuilder(ChatManager::class)
|
||||
->setConstructorArgs([
|
||||
$this->commentsManager,
|
||||
$this->dispatcher,
|
||||
\OCP\Server::get(IDBConnection::class),
|
||||
$this->notificationManager,
|
||||
$this->shareManager,
|
||||
$this->shareProvider,
|
||||
$this->participantService,
|
||||
$this->roomService,
|
||||
$this->pollService,
|
||||
$this->threadService,
|
||||
$this->notifier,
|
||||
$cacheFactory,
|
||||
$this->timeFactory,
|
||||
$this->attachmentService,
|
||||
$this->referenceManager,
|
||||
$this->rateLimiter,
|
||||
$this->request,
|
||||
$this->l,
|
||||
$this->logger,
|
||||
])
|
||||
->onlyMethods($methods)
|
||||
->getMock();
|
||||
}
|
||||
|
||||
return new ChatManager(
|
||||
$this->commentsManager,
|
||||
$this->dispatcher,
|
||||
\OCP\Server::get(IDBConnection::class),
|
||||
$this->notificationManager,
|
||||
$this->shareManager,
|
||||
$this->shareProvider,
|
||||
$this->participantService,
|
||||
$this->roomService,
|
||||
$this->pollService,
|
||||
$this->threadService,
|
||||
$this->notifier,
|
||||
$cacheFactory,
|
||||
$this->timeFactory,
|
||||
$this->attachmentService,
|
||||
$this->referenceManager,
|
||||
$this->rateLimiter,
|
||||
$this->request,
|
||||
$this->l,
|
||||
$this->logger,
|
||||
);
|
||||
}
|
||||
|
||||
private function newComment($id, string $actorType, string $actorId, \DateTime $creationDateTime, string $message): IComment {
|
||||
$comment = $this->createMock(IComment::class);
|
||||
|
||||
$id = (string)$id;
|
||||
|
||||
$comment->method('getId')->willReturn($id);
|
||||
$comment->method('getActorType')->willReturn($actorType);
|
||||
$comment->method('getActorId')->willReturn($actorId);
|
||||
$comment->method('getCreationDateTime')->willReturn($creationDateTime);
|
||||
$comment->method('getMessage')->willReturn($message);
|
||||
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return IComment|MockObject
|
||||
*/
|
||||
private function newCommentFromArray(array $data): IComment {
|
||||
$comment = $this->createMock(IComment::class);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if ($key === 'id') {
|
||||
$value = (string)$value;
|
||||
}
|
||||
$comment->method('get' . ucfirst($key))->willReturn($value);
|
||||
}
|
||||
|
||||
return $comment;
|
||||
}
|
||||
|
||||
protected function assertCommentEquals(array $data, IComment $comment): void {
|
||||
if (isset($data['id'])) {
|
||||
$id = $data['id'];
|
||||
unset($data['id']);
|
||||
$this->assertEquals($id, $comment->getId());
|
||||
}
|
||||
|
||||
$this->assertEquals($data, [
|
||||
'actorType' => $comment->getActorType(),
|
||||
'actorId' => $comment->getActorId(),
|
||||
'creationDateTime' => $comment->getCreationDateTime(),
|
||||
'message' => $comment->getMessage(),
|
||||
'referenceId' => $comment->getReferenceId(),
|
||||
'parentId' => $comment->getParentId(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function dataSendMessage(): array {
|
||||
return [
|
||||
'simple message' => ['testUser1', 'testMessage1', '', '0'],
|
||||
'reference id' => ['testUser2', 'testMessage2', 'referenceId2', '0'],
|
||||
'as a reply' => ['testUser3', 'testMessage3', '', '23'],
|
||||
'reply w/ ref' => ['testUser4', 'testMessage4', 'referenceId4', '23'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataSendMessage')]
|
||||
public function testSendMessage(string $userId, string $message, string $referenceId, string $parentId): void {
|
||||
$creationDateTime = new \DateTime();
|
||||
|
||||
$commentExpected = [
|
||||
'actorType' => 'users',
|
||||
'actorId' => $userId,
|
||||
'creationDateTime' => $creationDateTime,
|
||||
'message' => $message,
|
||||
'referenceId' => $referenceId,
|
||||
'parentId' => $parentId,
|
||||
];
|
||||
|
||||
$comment = $this->newCommentFromArray($commentExpected);
|
||||
|
||||
if ($parentId !== '0') {
|
||||
$replyTo = $this->newCommentFromArray([
|
||||
'id' => $parentId,
|
||||
]);
|
||||
|
||||
$comment->expects($this->once())
|
||||
->method('setParentId')
|
||||
->with($parentId);
|
||||
} else {
|
||||
$replyTo = null;
|
||||
}
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('create')
|
||||
->with('users', $userId, 'chat', 1234)
|
||||
->willReturn($comment);
|
||||
|
||||
$comment->expects($this->once())
|
||||
->method('setMessage')
|
||||
->with($message);
|
||||
|
||||
$comment->expects($this->once())
|
||||
->method('setCreationDateTime')
|
||||
->with($creationDateTime);
|
||||
|
||||
$comment->expects($referenceId === '' ? $this->never() : $this->once())
|
||||
->method('setReferenceId')
|
||||
->with($referenceId);
|
||||
|
||||
$comment->expects($this->once())
|
||||
->method('setVerb')
|
||||
->with('comment');
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('save')
|
||||
->with($comment);
|
||||
|
||||
$chat = $this->createMock(Room::class);
|
||||
$chat->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(1234);
|
||||
|
||||
$this->notifier->expects($this->once())
|
||||
->method('notifyMentionedUsers')
|
||||
->with($chat, $comment);
|
||||
|
||||
$participant = $this->createMock(Participant::class);
|
||||
|
||||
$return = $this->chatManager->sendMessage($chat, $participant, 'users', $userId, $message, $creationDateTime, $replyTo, $referenceId, false);
|
||||
|
||||
$this->assertCommentEquals($commentExpected, $return);
|
||||
}
|
||||
|
||||
public function testGetHistory(): void {
|
||||
$offset = 1;
|
||||
$limit = 42;
|
||||
$expected = [
|
||||
$this->newComment(110, 'users', 'testUnknownUser', new \DateTime('@' . 1000000042), 'testMessage3'),
|
||||
$this->newComment(109, 'guests', 'testSpreedSession', new \DateTime('@' . 1000000023), 'testMessage2'),
|
||||
$this->newComment(108, 'users', 'testUser', new \DateTime('@' . 1000000016), 'testMessage1')
|
||||
];
|
||||
|
||||
$chat = $this->createMock(Room::class);
|
||||
$chat->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(1234);
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('getCommentsWithVerbForObjectSinceComment')
|
||||
->with('chat', 1234, [], $offset, 'desc', $limit)
|
||||
->willReturn($expected);
|
||||
|
||||
$comments = $this->chatManager->getHistory($chat, $offset, $limit, false);
|
||||
|
||||
$this->assertEquals($expected, $comments);
|
||||
}
|
||||
|
||||
public function testWaitForNewMessages(): void {
|
||||
$offset = 1;
|
||||
$limit = 42;
|
||||
$timeout = 23;
|
||||
$expected = [
|
||||
$this->newComment(108, 'users', 'testUser', new \DateTime('@' . 1000000016), 'testMessage1'),
|
||||
$this->newComment(109, 'guests', 'testSpreedSession', new \DateTime('@' . 1000000023), 'testMessage2'),
|
||||
$this->newComment(110, 'users', 'testUnknownUser', new \DateTime('@' . 1000000042), 'testMessage3'),
|
||||
];
|
||||
|
||||
$chat = $this->createMock(Room::class);
|
||||
$chat->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(1234);
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('getCommentsWithVerbForObjectSinceComment')
|
||||
->with('chat', 1234, [], $offset, 'asc', $limit)
|
||||
->willReturn($expected);
|
||||
|
||||
$this->notifier->expects($this->once())
|
||||
->method('markMentionNotificationsRead')
|
||||
->with($chat, 'userId');
|
||||
|
||||
/** @var IUser&MockObject $user */
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->willReturn('userId');
|
||||
|
||||
$comments = $this->chatManager->waitForNewMessages($chat, $offset, $limit, $timeout, $user, false, true);
|
||||
|
||||
$this->assertEquals($expected, $comments);
|
||||
}
|
||||
|
||||
public function testWaitForNewMessagesWithWaiting(): void {
|
||||
$offset = 1;
|
||||
$limit = 42;
|
||||
$timeout = 23;
|
||||
$expected = [
|
||||
$this->newComment(108, 'users', 'testUser', new \DateTime('@' . 1000000016), 'testMessage1'),
|
||||
$this->newComment(109, 'guests', 'testSpreedSession', new \DateTime('@' . 1000000023), 'testMessage2'),
|
||||
$this->newComment(110, 'users', 'testUnknownUser', new \DateTime('@' . 1000000042), 'testMessage3'),
|
||||
];
|
||||
|
||||
$chat = $this->createMock(Room::class);
|
||||
$chat->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(1234);
|
||||
|
||||
$this->commentsManager->expects($this->exactly(2))
|
||||
->method('getCommentsWithVerbForObjectSinceComment')
|
||||
->with('chat', 1234, [], $offset, 'asc', $limit)
|
||||
->willReturnOnConsecutiveCalls(
|
||||
[],
|
||||
$expected
|
||||
);
|
||||
|
||||
$this->notifier->expects($this->once())
|
||||
->method('markMentionNotificationsRead')
|
||||
->with($chat, 'userId');
|
||||
|
||||
/** @var IUser&MockObject $user */
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->willReturn('userId');
|
||||
|
||||
$comments = $this->chatManager->waitForNewMessages($chat, $offset, $limit, $timeout, $user, false, true);
|
||||
|
||||
$this->assertEquals($expected, $comments);
|
||||
}
|
||||
|
||||
public function testGetUnreadCount(): void {
|
||||
/** @var Room&MockObject $chat */
|
||||
$chat = $this->createMock(Room::class);
|
||||
$chat->expects($this->atLeastOnce())
|
||||
->method('getId')
|
||||
->willReturn(23);
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('getNumberOfCommentsWithVerbsForObjectSinceComment')
|
||||
->with('chat', 23, 42, ['comment', 'object_shared']);
|
||||
|
||||
$this->chatManager->getUnreadCount($chat, 42);
|
||||
}
|
||||
|
||||
public function testDeleteMessages(): void {
|
||||
$chat = $this->createMock(Room::class);
|
||||
$chat->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(1234);
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('deleteCommentsAtObject')
|
||||
->with('chat', 1234);
|
||||
|
||||
$this->notifier->expects($this->once())
|
||||
->method('removePendingNotificationsForRoom')
|
||||
->with($chat);
|
||||
|
||||
$this->chatManager->deleteMessages($chat);
|
||||
}
|
||||
|
||||
public function testDeleteMessage(): void {
|
||||
$mapper = new AttendeeMapper(\OCP\Server::get(IDBConnection::class));
|
||||
$attendee = $mapper->createAttendeeFromRow([
|
||||
'a_id' => 1,
|
||||
'room_id' => 123,
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'user',
|
||||
'display_name' => 'user-display',
|
||||
'pin' => '',
|
||||
'participant_type' => Participant::USER,
|
||||
'favorite' => true,
|
||||
'notification_level' => Participant::NOTIFY_MENTION,
|
||||
'notification_calls' => Participant::NOTIFY_CALLS_ON,
|
||||
'last_joined_call' => 0,
|
||||
'last_read_message' => 0,
|
||||
'last_mention_message' => 0,
|
||||
'last_mention_direct' => 0,
|
||||
'read_privacy' => Participant::PRIVACY_PUBLIC,
|
||||
'permissions' => Attendee::PERMISSIONS_DEFAULT,
|
||||
'access_token' => '',
|
||||
'remote_id' => '',
|
||||
'phone_number' => '',
|
||||
'call_id' => '',
|
||||
'invited_cloud_id' => '',
|
||||
'state' => Invitation::STATE_ACCEPTED,
|
||||
'unread_messages' => 0,
|
||||
'last_attendee_activity' => 0,
|
||||
'archived' => 0,
|
||||
'important' => 0,
|
||||
'sensitive' => 0,
|
||||
'has_unread_threads' => false,
|
||||
'has_unread_thread_mentions' => false,
|
||||
'has_unread_thread_directs' => false,
|
||||
]);
|
||||
$chat = $this->createMock(Room::class);
|
||||
$chat->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(1234);
|
||||
$participant = new Participant($chat, $attendee, null);
|
||||
|
||||
$date = new \DateTime();
|
||||
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getId')
|
||||
->willReturn('123456');
|
||||
$comment->method('getVerb')
|
||||
->willReturn('comment');
|
||||
$comment->expects($this->once())
|
||||
->method('setMessage');
|
||||
$comment->expects($this->once())
|
||||
->method('setVerb')
|
||||
->with('comment_deleted');
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('save')
|
||||
->with($comment);
|
||||
|
||||
$systemMessage = $this->createMock(IComment::class);
|
||||
|
||||
$chatManager = $this->getManager(['addSystemMessage']);
|
||||
$chatManager->expects($this->once())
|
||||
->method('addSystemMessage')
|
||||
->with($chat, $participant, Attendee::ACTOR_USERS, 'user', $this->anything(), $this->anything(), false, null, $comment)
|
||||
->willReturn($systemMessage);
|
||||
|
||||
$this->assertSame($systemMessage, $chatManager->deleteMessage($chat, $comment, $participant, $date));
|
||||
}
|
||||
|
||||
public function testDeleteMessageFileShare(): void {
|
||||
$mapper = new AttendeeMapper(\OCP\Server::get(IDBConnection::class));
|
||||
$attendee = $mapper->createAttendeeFromRow([
|
||||
'a_id' => 1,
|
||||
'room_id' => 123,
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'user',
|
||||
'display_name' => 'user-display',
|
||||
'pin' => '',
|
||||
'participant_type' => Participant::USER,
|
||||
'favorite' => true,
|
||||
'notification_level' => Participant::NOTIFY_MENTION,
|
||||
'notification_calls' => Participant::NOTIFY_CALLS_ON,
|
||||
'last_joined_call' => 0,
|
||||
'last_read_message' => 0,
|
||||
'last_mention_message' => 0,
|
||||
'last_mention_direct' => 0,
|
||||
'read_privacy' => Participant::PRIVACY_PUBLIC,
|
||||
'permissions' => Attendee::PERMISSIONS_DEFAULT,
|
||||
'access_token' => '',
|
||||
'remote_id' => '',
|
||||
'phone_number' => '',
|
||||
'call_id' => '',
|
||||
'invited_cloud_id' => '',
|
||||
'state' => Invitation::STATE_ACCEPTED,
|
||||
'unread_messages' => 0,
|
||||
'last_attendee_activity' => 0,
|
||||
'archived' => 0,
|
||||
'important' => 0,
|
||||
'sensitive' => 0,
|
||||
'has_unread_threads' => false,
|
||||
'has_unread_thread_mentions' => false,
|
||||
'has_unread_thread_directs' => false,
|
||||
]);
|
||||
$chat = $this->createMock(Room::class);
|
||||
$chat->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(1234);
|
||||
$chat->expects($this->any())
|
||||
->method('getToken')
|
||||
->willReturn('T0k3N');
|
||||
$participant = new Participant($chat, $attendee, null);
|
||||
|
||||
$date = new \DateTime();
|
||||
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getId')
|
||||
->willReturn('123456');
|
||||
$comment->method('getVerb')
|
||||
->willReturn('object_shared');
|
||||
$comment->expects($this->once())
|
||||
->method('getMessage')
|
||||
->willReturn(json_encode(['message' => 'file_shared', 'parameters' => ['share' => '42']]));
|
||||
$comment->expects($this->once())
|
||||
->method('setMessage');
|
||||
$comment->expects($this->once())
|
||||
->method('setVerb')
|
||||
->with('comment_deleted');
|
||||
|
||||
$share = $this->createMock(IShare::class);
|
||||
$share->method('getShareType')
|
||||
->willReturn(IShare::TYPE_ROOM);
|
||||
$share->method('getSharedWith')
|
||||
->willReturn('T0k3N');
|
||||
$share->method('getShareOwner')
|
||||
->willReturn('user');
|
||||
|
||||
$this->shareManager->method('getShareById')
|
||||
->with('ocRoomShare:42')
|
||||
->willReturn($share);
|
||||
|
||||
$this->shareManager->expects($this->once())
|
||||
->method('deleteShare')
|
||||
->willReturn($share);
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('save')
|
||||
->with($comment);
|
||||
|
||||
$systemMessage = $this->createMock(IComment::class);
|
||||
|
||||
$chatManager = $this->getManager(['addSystemMessage']);
|
||||
$chatManager->expects($this->once())
|
||||
->method('addSystemMessage')
|
||||
->with($chat, $participant, Attendee::ACTOR_USERS, 'user', $this->anything(), $this->anything(), false, null, $comment)
|
||||
->willReturn($systemMessage);
|
||||
|
||||
$this->assertSame($systemMessage, $chatManager->deleteMessage($chat, $comment, $participant, $date));
|
||||
}
|
||||
|
||||
public function testDeleteMessageFileShareNotFound(): void {
|
||||
$mapper = new AttendeeMapper(\OCP\Server::get(IDBConnection::class));
|
||||
$attendee = $mapper->createAttendeeFromRow([
|
||||
'a_id' => 1,
|
||||
'room_id' => 123,
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'user',
|
||||
'display_name' => 'user-display',
|
||||
'pin' => '',
|
||||
'participant_type' => Participant::USER,
|
||||
'favorite' => true,
|
||||
'notification_level' => Participant::NOTIFY_MENTION,
|
||||
'notification_calls' => Participant::NOTIFY_CALLS_ON,
|
||||
'last_joined_call' => 0,
|
||||
'last_read_message' => 0,
|
||||
'last_mention_message' => 0,
|
||||
'last_mention_direct' => 0,
|
||||
'read_privacy' => Participant::PRIVACY_PUBLIC,
|
||||
'permissions' => Attendee::PERMISSIONS_DEFAULT,
|
||||
'access_token' => '',
|
||||
'remote_id' => '',
|
||||
'phone_number' => '',
|
||||
'call_id' => '',
|
||||
'invited_cloud_id' => '',
|
||||
'state' => Invitation::STATE_ACCEPTED,
|
||||
'unread_messages' => 0,
|
||||
'last_attendee_activity' => 0,
|
||||
'archived' => 0,
|
||||
'important' => 0,
|
||||
'sensitive' => 0,
|
||||
'has_unread_threads' => false,
|
||||
'has_unread_thread_mentions' => false,
|
||||
'has_unread_thread_directs' => false,
|
||||
]);
|
||||
$chat = $this->createMock(Room::class);
|
||||
$chat->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(1234);
|
||||
$participant = new Participant($chat, $attendee, null);
|
||||
|
||||
$date = new \DateTime();
|
||||
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getId')
|
||||
->willReturn('123456');
|
||||
$comment->method('getVerb')
|
||||
->willReturn('object_shared');
|
||||
$comment->expects($this->once())
|
||||
->method('getMessage')
|
||||
->willReturn(json_encode(['message' => 'file_shared', 'parameters' => ['share' => '42']]));
|
||||
|
||||
$this->shareManager->method('getShareById')
|
||||
->with('ocRoomShare:42')
|
||||
->willThrowException(new ShareNotFound());
|
||||
|
||||
$this->commentsManager->expects($this->never())
|
||||
->method('save');
|
||||
|
||||
$systemMessage = $this->createMock(IComment::class);
|
||||
|
||||
$chatManager = $this->getManager(['addSystemMessage']);
|
||||
$chatManager->expects($this->never())
|
||||
->method('addSystemMessage');
|
||||
|
||||
$this->expectException(ShareNotFound::class);
|
||||
$this->assertSame($systemMessage, $chatManager->deleteMessage($chat, $comment, $participant, $date));
|
||||
}
|
||||
|
||||
public function testClearHistory(): void {
|
||||
$chat = $this->createMock(Room::class);
|
||||
$chat->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(1234);
|
||||
$chat->expects($this->any())
|
||||
->method('getToken')
|
||||
->willReturn('t0k3n');
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('deleteCommentsAtObject')
|
||||
->with('chat', 1234);
|
||||
|
||||
$this->shareProvider->expects($this->once())
|
||||
->method('deleteInRoom')
|
||||
->with('t0k3n');
|
||||
|
||||
$this->notifier->expects($this->once())
|
||||
->method('removePendingNotificationsForRoom')
|
||||
->with($chat, true);
|
||||
|
||||
$this->participantService->expects($this->once())
|
||||
->method('resetChatDetails')
|
||||
->with($chat);
|
||||
|
||||
$date = new \DateTime();
|
||||
$this->timeFactory->method('getDateTime')
|
||||
->willReturn($date);
|
||||
|
||||
$manager = $this->getManager(['addSystemMessage']);
|
||||
$manager->expects($this->once())
|
||||
->method('addSystemMessage')
|
||||
->with(
|
||||
$chat,
|
||||
null,
|
||||
'users',
|
||||
'admin',
|
||||
json_encode(['message' => 'history_cleared', 'parameters' => []]),
|
||||
$date,
|
||||
false
|
||||
);
|
||||
$manager->clearHistory($chat, 'users', 'admin');
|
||||
}
|
||||
|
||||
public static function dataSearchIsPartOfConversationNameOrAtAll(): array {
|
||||
return [
|
||||
'found a in all' => [
|
||||
'a', 'room', true
|
||||
],
|
||||
'found h in here' => [
|
||||
'h', 'room', true
|
||||
],
|
||||
'case sensitive, not found A in all' => [
|
||||
'A', 'room', false
|
||||
],
|
||||
'case sensitive, not found H in here' => [
|
||||
'H', 'room', false
|
||||
],
|
||||
'non case sensitive, found r in room' => [
|
||||
'R', 'room', true
|
||||
],
|
||||
'found r in begin of room' => [
|
||||
'r', 'room', true
|
||||
],
|
||||
'found o in middle of room' => [
|
||||
'o', 'room', true
|
||||
],
|
||||
'not found all in middle of text' => [
|
||||
'notbeginingall', 'room', false
|
||||
],
|
||||
'not found here in middle of text' => [
|
||||
'notbegininghere', 'room', false
|
||||
],
|
||||
'not found room in middle of text' => [
|
||||
'notbeginingroom', 'room', false
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataSearchIsPartOfConversationNameOrAtAll')]
|
||||
public function testSearchIsPartOfConversationNameOrAtAll(string $search, string $roomDisplayName, bool $expected): void {
|
||||
$actual = self::invokePrivate($this->chatManager, 'searchIsPartOfConversationNameOrAtAll', [$search, $roomDisplayName]);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public static function dataAddConversationNotify(): array {
|
||||
return [
|
||||
[
|
||||
'',
|
||||
['getType' => Room::TYPE_ONE_TO_ONE],
|
||||
[],
|
||||
null,
|
||||
[],
|
||||
],
|
||||
[
|
||||
'',
|
||||
['getDisplayName' => 'test', 'getMentionPermissions' => 0],
|
||||
['getAttendee' => Attendee::fromRow([
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'user',
|
||||
])],
|
||||
324,
|
||||
[['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all', 'details' => 'All 324 participants']]
|
||||
],
|
||||
[
|
||||
'',
|
||||
['getMentionPermissions' => 1],
|
||||
['hasModeratorPermissions' => false],
|
||||
null,
|
||||
[],
|
||||
],
|
||||
[
|
||||
'all',
|
||||
['getDisplayName' => 'test', 'getMentionPermissions' => 0],
|
||||
['getAttendee' => Attendee::fromRow([
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'user',
|
||||
])],
|
||||
1,
|
||||
[['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all']],
|
||||
],
|
||||
[
|
||||
'all',
|
||||
['getDisplayName' => 'test', 'getMentionPermissions' => 1],
|
||||
[
|
||||
'getAttendee' => Attendee::fromRow([
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'user',
|
||||
]),
|
||||
'hasModeratorPermissions' => true,
|
||||
],
|
||||
8,
|
||||
[['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all', 'details' => 'All 8 participants']],
|
||||
],
|
||||
[
|
||||
'here',
|
||||
['getDisplayName' => 'test', 'getMentionPermissions' => 0],
|
||||
['getAttendee' => Attendee::fromRow([
|
||||
'actor_type' => Attendee::ACTOR_GUESTS,
|
||||
'actor_id' => 'guest',
|
||||
])],
|
||||
12,
|
||||
[['id' => 'all', 'label' => 'test', 'source' => 'calls', 'mentionId' => 'all', 'details' => 'All 12 participants']],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataAddConversationNotify')]
|
||||
public function testAddConversationNotify(string $search, array $roomMocks, array $participantMocks, ?int $totalCount, array $expected): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
foreach ($roomMocks as $method => $return) {
|
||||
$room->expects($this->once())
|
||||
->method($method)
|
||||
->willReturn($return);
|
||||
}
|
||||
|
||||
$participant = $this->createMock(Participant::class);
|
||||
foreach ($participantMocks as $method => $return) {
|
||||
$participant->expects($this->once())
|
||||
->method($method)
|
||||
->willReturn($return);
|
||||
}
|
||||
|
||||
if ($totalCount !== null) {
|
||||
$this->participantService->method('getNumberOfUsers')
|
||||
->willReturn($totalCount);
|
||||
}
|
||||
|
||||
$actual = $this->chatManager->addConversationNotify([], $search, $room, $participant);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
#[DataProvider('dataIsSharedFile')]
|
||||
public function testIsSharedFile(string $message, bool $expected): void {
|
||||
$actual = $this->chatManager->isSharedFile($message);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public static function dataIsSharedFile(): array {
|
||||
return [
|
||||
['', false],
|
||||
[json_encode([]), false],
|
||||
[json_encode(['parameters' => '']), false],
|
||||
[json_encode(['parameters' => []]), false],
|
||||
[json_encode(['parameters' => ['share' => null]]), false],
|
||||
[json_encode(['parameters' => ['share' => '']]), false],
|
||||
[json_encode(['parameters' => ['share' => []]]), false],
|
||||
[json_encode(['parameters' => ['share' => 0]]), false],
|
||||
[json_encode(['parameters' => ['share' => 1]]), true],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataFilterCommentsWithNonExistingFiles')]
|
||||
public function testFilterCommentsWithNonExistingFiles(array $list, int $expectedCount): void {
|
||||
// Transform text messages in instance of comment and mock with the message
|
||||
foreach ($list as $key => $message) {
|
||||
$list[$key] = $this->createMock(IComment::class);
|
||||
$list[$key]->method('getMessage')
|
||||
->willReturn($message);
|
||||
$messageDecoded = json_decode($message, true);
|
||||
if (isset($messageDecoded['parameters']['share']) && $messageDecoded['parameters']['share'] === 'notExists') {
|
||||
$this->shareProvider->expects($this->once())
|
||||
->method('getShareById')
|
||||
->with('notExists')
|
||||
->willThrowException(new ShareNotFound());
|
||||
}
|
||||
}
|
||||
if (count($list) !== $expectedCount) {
|
||||
}
|
||||
$result = $this->chatManager->filterCommentsWithNonExistingFiles($list);
|
||||
$this->assertCount($expectedCount, $result);
|
||||
}
|
||||
|
||||
public static function dataFilterCommentsWithNonExistingFiles(): array {
|
||||
return [
|
||||
[[], 0],
|
||||
[[json_encode(['parameters' => ['not a shared file']])], 1],
|
||||
[[json_encode(['parameters' => ['share' => 'notExists']])], 0],
|
||||
[[json_encode(['parameters' => ['share' => 1]])], 1],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Chat;
|
||||
|
||||
use OC\Comments\Comment;
|
||||
use OCA\Talk\Chat\Notifier;
|
||||
use OCA\Talk\Exceptions\ParticipantNotFoundException;
|
||||
use OCA\Talk\Files\Util;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Model\Session;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\ThreadService;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Comments\IComment;
|
||||
use OCP\IConfig;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Notification\IManager as INotificationManager;
|
||||
use OCP\Notification\INotification;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class NotifierTest extends TestCase {
|
||||
protected INotificationManager&MockObject $notificationManager;
|
||||
protected IUserManager&MockObject $userManager;
|
||||
protected IGroupManager&MockObject $groupManager;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected ThreadService&MockObject $threadService;
|
||||
protected IConfig&MockObject $config;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected Util&MockObject $util;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->notificationManager = $this->createMock(INotificationManager::class);
|
||||
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->userManager
|
||||
->method('userExists')
|
||||
->willReturnCallback(function ($userId) {
|
||||
return $userId !== 'unknownUser';
|
||||
});
|
||||
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->threadService = $this->createMock(ThreadService::class);
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->util = $this->createMock(Util::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $methods
|
||||
* @return Notifier|MockObject
|
||||
*/
|
||||
protected function getNotifier(array $methods = []) {
|
||||
if (!empty($methods)) {
|
||||
return $this->getMockBuilder(Notifier::class)
|
||||
->setConstructorArgs([
|
||||
$this->notificationManager,
|
||||
$this->userManager,
|
||||
$this->groupManager,
|
||||
$this->participantService,
|
||||
$this->threadService,
|
||||
$this->config,
|
||||
$this->timeFactory,
|
||||
$this->util,
|
||||
])
|
||||
->onlyMethods($methods)
|
||||
->getMock();
|
||||
}
|
||||
return new Notifier(
|
||||
$this->notificationManager,
|
||||
$this->userManager,
|
||||
$this->groupManager,
|
||||
$this->participantService,
|
||||
$this->threadService,
|
||||
$this->config,
|
||||
$this->timeFactory,
|
||||
$this->util
|
||||
);
|
||||
}
|
||||
|
||||
private function newComment($id, $actorType, $actorId, $creationDateTime, $message): IComment {
|
||||
$comment = new Comment([
|
||||
'id' => $id,
|
||||
'object_id' => '1234',
|
||||
'object_type' => 'chat',
|
||||
'actor_type' => $actorType,
|
||||
'actor_id' => $actorId,
|
||||
'creation_date_time' => $creationDateTime,
|
||||
'message' => $message,
|
||||
'verb' => 'comment',
|
||||
]);
|
||||
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Room|MockObject
|
||||
*/
|
||||
private function getRoom($settings = []) {
|
||||
/** @var Room|MockObject */
|
||||
$room = $this->createMock(Room::class);
|
||||
|
||||
$this->participantService->expects($this->any())
|
||||
->method('getParticipant')
|
||||
->willReturnCallback(function (Room $room, string $actorId) use ($settings): Participant {
|
||||
if ($actorId === 'userNotInOneToOneChat') {
|
||||
throw new ParticipantNotFoundException();
|
||||
}
|
||||
$attendeeRow = [
|
||||
'actor_type' => 'user',
|
||||
'actor_id' => $actorId,
|
||||
];
|
||||
if (isset($settings['attendee'][$actorId])) {
|
||||
$attendeeRow = array_merge($attendeeRow, $settings['attendee'][$actorId]);
|
||||
}
|
||||
$attendee = Attendee::fromRow($attendeeRow);
|
||||
return new Participant($room, $attendee, null);
|
||||
});
|
||||
|
||||
return $room;
|
||||
}
|
||||
|
||||
public static function dataNotifyMentionedUsers(): array {
|
||||
return [
|
||||
'no notifications' => [
|
||||
'No mentions', [], [], [],
|
||||
],
|
||||
'notify a mentioned user' => [
|
||||
'Mention @anotherUser', [], [['id' => 'anotherUser', 'type' => 'users', 'reason' => 'direct']], [['id' => 'anotherUser', 'type' => 'users', 'reason' => 'direct']],
|
||||
],
|
||||
'not notify mentioned user if already notified' => [
|
||||
'Mention @anotherUser', [['id' => 'anotherUser', 'type' => 'users', 'reason' => 'reply']], [], [['id' => 'anotherUser', 'type' => 'users', 'reason' => 'reply']],
|
||||
],
|
||||
'notify mentioned Users With Long Message Start Mention' => [
|
||||
'123456789 @anotherUserWithOddLengthName 123456789-123456789-123456789-123456789-123456789-123456789', [], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']],
|
||||
],
|
||||
'notify mentioned users with long message middle mention' => [
|
||||
'123456789-123456789-123456789-1234 @anotherUserWithOddLengthName 6789-123456789-123456789-123456789', [], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']],
|
||||
],
|
||||
'notify mentioned users with long message end mention' => [
|
||||
'123456789-123456789-123456789-123456789-123456789-123456789 @anotherUserWithOddLengthName 123456789', [], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']],
|
||||
],
|
||||
'mention herself' => [
|
||||
'Mention @testUser', [], [], [],
|
||||
],
|
||||
'not notify unknownuser' => [
|
||||
'Mention @unknownUser', [], [], [],
|
||||
],
|
||||
'notify mentioned users several mentions' => [
|
||||
'Mention @anotherUser, and @unknownUser, and @testUser, and @userAbleToJoin', [],
|
||||
[['id' => 'anotherUser', 'type' => 'users', 'reason' => 'direct'], ['id' => 'userAbleToJoin', 'type' => 'users', 'reason' => 'direct']],
|
||||
[['id' => 'anotherUser', 'type' => 'users', 'reason' => 'direct'], ['id' => 'userAbleToJoin', 'type' => 'users', 'reason' => 'direct']],
|
||||
],
|
||||
'notify mentioned users to user not invited to chat' => [
|
||||
'Mention @userNotInOneToOneChat', [], [], [],
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataNotifyMentionedUsers')]
|
||||
public function testNotifyMentionedUsers(string $message, array $alreadyNotifiedUsers, array $notify, array $expectedReturn): void {
|
||||
if (count($notify)) {
|
||||
$this->notificationManager->expects($this->exactly(count($notify)))
|
||||
->method('notify');
|
||||
}
|
||||
|
||||
$room = $this->getRoom();
|
||||
$comment = $this->newComment('108', 'users', 'testUser', new \DateTime('@' . 1000000016), $message);
|
||||
$notifier = $this->getNotifier([]);
|
||||
$participant = $this->createMock(Participant::class);
|
||||
$actual = $notifier->notifyMentionedUsers($room, $comment, $alreadyNotifiedUsers, false, $participant);
|
||||
|
||||
$this->assertEqualsCanonicalizing($expectedReturn, $actual);
|
||||
}
|
||||
|
||||
public static function dataShouldParticipantBeNotified(): array {
|
||||
return [
|
||||
[Attendee::ACTOR_GROUPS, 'test1', null, Attendee::ACTOR_USERS, 'test1', [], false, Notifier::PRIORITY_NONE],
|
||||
[Attendee::ACTOR_USERS, 'test1', null, Attendee::ACTOR_USERS, 'test1', [], false, Notifier::PRIORITY_NONE],
|
||||
[Attendee::ACTOR_USERS, 'test1', null, Attendee::ACTOR_USERS, 'test2', [], false, Notifier::PRIORITY_NORMAL],
|
||||
[Attendee::ACTOR_USERS, 'test1', null, Attendee::ACTOR_USERS, 'test2', [['id' => 'test1', 'type' => Attendee::ACTOR_USERS]], false, Notifier::PRIORITY_NONE],
|
||||
[Attendee::ACTOR_USERS, 'test1', null, Attendee::ACTOR_USERS, 'test2', [['id' => 'test1', 'type' => Attendee::ACTOR_FEDERATED_USERS]], false, Notifier::PRIORITY_NORMAL],
|
||||
[Attendee::ACTOR_USERS, 'test1', Session::SESSION_TIMEOUT - 5, Attendee::ACTOR_USERS, 'test2', [], false, Notifier::PRIORITY_NONE],
|
||||
[Attendee::ACTOR_USERS, 'test1', Session::SESSION_TIMEOUT + 5, Attendee::ACTOR_USERS, 'test2', [], false, Notifier::PRIORITY_NORMAL],
|
||||
|
||||
// Marked as important, still blocked by session and being the author, but otherwise with PRIORITY_IMPORTANT
|
||||
[Attendee::ACTOR_USERS, 'test1', null, Attendee::ACTOR_USERS, 'test1', [], true, Notifier::PRIORITY_NONE],
|
||||
[Attendee::ACTOR_USERS, 'test1', null, Attendee::ACTOR_USERS, 'test2', [], true, Notifier::PRIORITY_IMPORTANT],
|
||||
[Attendee::ACTOR_USERS, 'test1', Session::SESSION_TIMEOUT - 5, Attendee::ACTOR_USERS, 'test2', [], true, Notifier::PRIORITY_NONE],
|
||||
[Attendee::ACTOR_USERS, 'test1', Session::SESSION_TIMEOUT + 5, Attendee::ACTOR_USERS, 'test2', [], true, Notifier::PRIORITY_IMPORTANT],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataShouldParticipantBeNotified')]
|
||||
public function testShouldParticipantBeNotified(string $actorType, string $actorId, ?int $sessionAge, string $commentActorType, string $commentActorId, array $alreadyNotifiedUsers, bool $isImportant, int $expected): void {
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getActorType')
|
||||
->willReturn($commentActorType);
|
||||
$comment->method('getActorId')
|
||||
->willReturn($commentActorId);
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$attendee = Attendee::fromRow([
|
||||
'actor_type' => $actorType,
|
||||
'actor_id' => $actorId,
|
||||
'important' => $isImportant,
|
||||
]);
|
||||
$session = null;
|
||||
if ($sessionAge !== null) {
|
||||
$current = 1234567;
|
||||
$this->timeFactory->method('getTime')
|
||||
->willReturn($current);
|
||||
|
||||
$session = Session::fromRow([
|
||||
'last_ping' => $current - $sessionAge,
|
||||
]);
|
||||
}
|
||||
$participant = new Participant($room, $attendee, $session);
|
||||
|
||||
self::assertSame($expected, self::invokePrivate($this->getNotifier(), 'shouldParticipantBeNotified', [$participant, $comment, $alreadyNotifiedUsers]));
|
||||
}
|
||||
|
||||
public function testRemovePendingNotificationsForRoom(): void {
|
||||
$notification = $this->createMock(INotification::class);
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->expects($this->any())
|
||||
->method('getToken')
|
||||
->willReturn('Token123');
|
||||
|
||||
$this->notificationManager->expects($this->once())
|
||||
->method('createNotification')
|
||||
->willReturn($notification);
|
||||
|
||||
$notification->expects($this->once())
|
||||
->method('setApp')
|
||||
->with('spreed')
|
||||
->willReturnSelf();
|
||||
|
||||
$notification->expects($this->atLeastOnce())
|
||||
->method('setObject')
|
||||
->with($this->anything(), 'Token123')
|
||||
->willReturnSelf();
|
||||
|
||||
$this->notificationManager->expects($this->atLeastOnce())
|
||||
->method('markProcessed')
|
||||
->with($notification);
|
||||
|
||||
$this->getNotifier()->removePendingNotificationsForRoom($room);
|
||||
}
|
||||
|
||||
public function testRemovePendingNotificationsForChatOnly(): void {
|
||||
$notification = $this->createMock(INotification::class);
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->expects($this->any())
|
||||
->method('getToken')
|
||||
->willReturn('Token123');
|
||||
|
||||
$this->notificationManager->expects($this->once())
|
||||
->method('createNotification')
|
||||
->willReturn($notification);
|
||||
|
||||
$notification->expects($this->once())
|
||||
->method('setApp')
|
||||
->with('spreed')
|
||||
->willReturnSelf();
|
||||
|
||||
$notification->expects($this->atLeastOnce())
|
||||
->method('setObject')
|
||||
->with($this->anything(), 'Token123')
|
||||
->willReturnSelf();
|
||||
|
||||
$this->notificationManager->expects($this->atLeastOnce())
|
||||
->method('markProcessed')
|
||||
->with($notification);
|
||||
|
||||
$this->getNotifier()->removePendingNotificationsForRoom($room, true);
|
||||
}
|
||||
|
||||
public static function dataAddMentionAllToList(): array {
|
||||
return [
|
||||
'not notify' => [
|
||||
[],
|
||||
[],
|
||||
0,
|
||||
true,
|
||||
[],
|
||||
],
|
||||
'preserve notify list and do not notify all' => [
|
||||
[
|
||||
['id' => 'user1', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
],
|
||||
[],
|
||||
0,
|
||||
true,
|
||||
[
|
||||
['id' => 'user1', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
],
|
||||
],
|
||||
'mention all' => [
|
||||
[
|
||||
['id' => 'user1', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
['id' => 'all', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
],
|
||||
[
|
||||
Attendee::fromRow(['actor_id' => 'user1', 'actor_type' => Attendee::ACTOR_USERS]),
|
||||
Attendee::fromRow(['actor_id' => 'user2', 'actor_type' => Attendee::ACTOR_USERS]),
|
||||
],
|
||||
0,
|
||||
false,
|
||||
[
|
||||
['id' => 'user1', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
['id' => 'user2', 'type' => Attendee::ACTOR_USERS, 'reason' => 'all'],
|
||||
],
|
||||
],
|
||||
'prevent non-moderator to notify all' => [
|
||||
[
|
||||
['id' => 'user1', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
['id' => 'all', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
],
|
||||
[
|
||||
Attendee::fromRow(['actor_id' => 'user1', 'actor_type' => Attendee::ACTOR_USERS]),
|
||||
Attendee::fromRow(['actor_id' => 'user2', 'actor_type' => Attendee::ACTOR_USERS]),
|
||||
],
|
||||
1,
|
||||
false,
|
||||
[
|
||||
['id' => 'user1', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataAddMentionAllToList')]
|
||||
public function testAddMentionAllToList(array $usersToNotify, array $participants, int $mentionPermissions, bool $moderatorPermissions, array $return): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getMentionPermissions')
|
||||
->willReturn($mentionPermissions);
|
||||
|
||||
$this->participantService
|
||||
->method('getActorsByType')
|
||||
->willReturn($participants);
|
||||
|
||||
$participant = $this->createMock(Participant::class);
|
||||
$participant->method('hasModeratorPermissions')
|
||||
->willReturn($moderatorPermissions);
|
||||
|
||||
$actual = self::invokePrivate($this->getNotifier(), 'addMentionAllToList', [$room, $usersToNotify, $participant]);
|
||||
$this->assertCount(count($return), $actual);
|
||||
foreach ($actual as $key => $value) {
|
||||
$this->assertIsArray($value);
|
||||
if (array_key_exists('attendee', $value)) {
|
||||
$this->assertInstanceOf(Attendee::class, $value['attendee']);
|
||||
unset($value['attendee']);
|
||||
}
|
||||
$this->assertEqualsCanonicalizing($return[$key], $value);
|
||||
}
|
||||
}
|
||||
|
||||
public static function dataNotifyReacted(): array {
|
||||
return [
|
||||
'author react to own message'
|
||||
=> [0, Participant::NOTIFY_MENTION, Room::TYPE_GROUP, 'testUser'],
|
||||
'notify never'
|
||||
=> [0, Participant::NOTIFY_NEVER, Room::TYPE_GROUP, 'testUser2'],
|
||||
'notify default, not one to one'
|
||||
=> [0, Participant::NOTIFY_DEFAULT, Room::TYPE_GROUP, 'testUser2'],
|
||||
'notify default, one to one'
|
||||
=> [1, Participant::NOTIFY_DEFAULT, Room::TYPE_ONE_TO_ONE, 'testUser2'],
|
||||
'notify always'
|
||||
=> [1, Participant::NOTIFY_ALWAYS, Room::TYPE_GROUP, 'testUser2'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataNotifyReacted')]
|
||||
public function testNotifyReacted(int $notify, int $notifyType, int $roomType, string $authorId): void {
|
||||
$this->notificationManager->expects($this->exactly($notify))
|
||||
->method('notify');
|
||||
|
||||
$room = $this->getRoom([
|
||||
'attendee' => [
|
||||
'testUser' => [
|
||||
'notificationLevel' => $notifyType,
|
||||
]
|
||||
]
|
||||
]);
|
||||
$room->method('getType')
|
||||
->willReturn($roomType);
|
||||
$comment = $this->newComment('108', 'users', 'testUser', new \DateTime('@' . 1000000016), 'message');
|
||||
$reaction = $this->newComment('108', 'users', $authorId, new \DateTime('@' . 1000000016), 'message');
|
||||
|
||||
$notifier = $this->getNotifier([]);
|
||||
$notifier->notifyReacted($room, $comment, $reaction);
|
||||
}
|
||||
|
||||
public static function dataGetMentionedUsers(): array {
|
||||
return [
|
||||
'mention one user' => [
|
||||
'Mention @anotherUser',
|
||||
[
|
||||
['id' => 'anotherUser', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
],
|
||||
],
|
||||
'mention two user' => [
|
||||
'Mention @anotherUser, and @unknownUser',
|
||||
[
|
||||
['id' => 'anotherUser', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
['id' => 'unknownUser', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
],
|
||||
],
|
||||
'mention all' => [
|
||||
'Mention @all',
|
||||
[
|
||||
['id' => 'all', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
],
|
||||
],
|
||||
'mention user, all, guest and group' => [
|
||||
'mention @test, @all, @"guest/1" @"group/1"',
|
||||
[
|
||||
['id' => 'test', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
['id' => 'all', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataGetMentionedUsers')]
|
||||
public function testGetMentionedUsers(string $message, array $expectedReturn): void {
|
||||
$comment = $this->newComment('108', 'users', 'testUser', new \DateTime('@' . 1000000016), $message);
|
||||
$actual = self::invokePrivate($this->getNotifier(), 'getMentionedUsers', [$comment]);
|
||||
$this->assertEqualsCanonicalizing($expectedReturn, $actual);
|
||||
}
|
||||
|
||||
public static function dataGetMentionedUserIds(): array {
|
||||
$return = self::dataGetMentionedUsers();
|
||||
array_walk($return, function (array &$scenario) {
|
||||
array_walk($scenario[1], function (array &$params): void {
|
||||
$params = $params['id'];
|
||||
});
|
||||
return $scenario;
|
||||
});
|
||||
return $return;
|
||||
}
|
||||
|
||||
#[DataProvider('dataGetMentionedUserIds')]
|
||||
public function testGetMentionedUserIds(string $message, array $expectedReturn): void {
|
||||
$comment = $this->newComment('108', 'users', 'testUser', new \DateTime('@' . 1000000016), $message);
|
||||
$actual = self::invokePrivate($this->getNotifier(), 'getMentionedUserIds', [$comment]);
|
||||
$this->assertEqualsCanonicalizing($expectedReturn, $actual);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,618 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Chat\Parser;
|
||||
|
||||
use OCA\Talk\Chat\Parser\UserMention;
|
||||
use OCA\Talk\Exceptions\ParticipantNotFoundException;
|
||||
use OCA\Talk\GuestManager;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Model\Message;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\AvatarService;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\Comments\IComment;
|
||||
use OCP\Comments\ICommentsManager;
|
||||
use OCP\Federation\ICloudId;
|
||||
use OCP\Federation\ICloudIdManager;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\IUserManager;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class UserMentionTest extends TestCase {
|
||||
protected IAppManager&MockObject $appManager;
|
||||
protected ICommentsManager&MockObject $commentsManager;
|
||||
protected IUserManager&MockObject $userManager;
|
||||
protected IGroupManager&MockObject $groupManager;
|
||||
protected GuestManager&MockObject $guestManager;
|
||||
protected AvatarService&MockObject $avatarService;
|
||||
protected ICloudIdManager&MockObject $cloudIdManager;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected IL10N&MockObject $l;
|
||||
|
||||
protected ?UserMention $parser = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->appManager = $this->createMock(IAppManager::class);
|
||||
$this->commentsManager = $this->createMock(ICommentsManager::class);
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||
$this->guestManager = $this->createMock(GuestManager::class);
|
||||
$this->avatarService = $this->createMock(AvatarService::class);
|
||||
$this->cloudIdManager = $this->createMock(ICloudIdManager::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->l = $this->createMock(IL10N::class);
|
||||
|
||||
$this->parser = new UserMention(
|
||||
$this->appManager,
|
||||
$this->commentsManager,
|
||||
$this->userManager,
|
||||
$this->groupManager,
|
||||
$this->guestManager,
|
||||
$this->avatarService,
|
||||
$this->cloudIdManager,
|
||||
$this->participantService,
|
||||
$this->l,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $mentions
|
||||
* @param array|null $metadata
|
||||
* @return MockObject|IComment
|
||||
*/
|
||||
private function newComment(array $mentions, ?array $metadata = null): IComment {
|
||||
$comment = $this->createMock(IComment::class);
|
||||
|
||||
$comment->method('getMentions')->willReturn($mentions);
|
||||
|
||||
if ($metadata !== null) {
|
||||
$comment->method('getMetaData')->willReturn($metadata);
|
||||
}
|
||||
|
||||
return $comment;
|
||||
}
|
||||
|
||||
public function testGetRichMessageWithoutEnrichableReferences(): void {
|
||||
$comment = $this->newComment([]);
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
$chatMessage->setMessage('Message without enrichable references', []);
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$this->assertEquals('Message without enrichable references', $chatMessage->getMessage());
|
||||
$this->assertEquals([], $chatMessage->getMessageParameters());
|
||||
}
|
||||
|
||||
public function testGetRichMessageWithSingleMention(): void {
|
||||
$mentions = [
|
||||
['type' => 'user', 'id' => 'testUser'],
|
||||
];
|
||||
$comment = $this->newComment($mentions);
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('resolveDisplayName')
|
||||
->with('user', 'testUser')
|
||||
->willReturn('testUser display name');
|
||||
|
||||
$this->userManager->expects($this->once())
|
||||
->method('getDisplayName')
|
||||
->with('testUser')
|
||||
->willReturn('testUser display name');
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
$chatMessage->setMessage('Mention to @testUser', []);
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$expectedMessageParameters = [
|
||||
'mention-user1' => [
|
||||
'type' => 'user',
|
||||
'id' => 'testUser',
|
||||
'name' => 'testUser display name',
|
||||
'mention-id' => 'testUser',
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals('Mention to {mention-user1}', $chatMessage->getMessage());
|
||||
$this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters());
|
||||
}
|
||||
|
||||
public function testGetRichMessageWithDuplicatedMention(): void {
|
||||
$mentions = [
|
||||
['type' => 'user', 'id' => 'testUser'],
|
||||
];
|
||||
$comment = $this->newComment($mentions);
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('resolveDisplayName')
|
||||
->with('user', 'testUser')
|
||||
->willReturn('testUser display name');
|
||||
|
||||
$this->userManager->expects($this->once())
|
||||
->method('getDisplayName')
|
||||
->with('testUser')
|
||||
->willReturn('testUser display name');
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
$chatMessage->setMessage('Mention to @testUser and @testUser again', []);
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$expectedMessageParameters = [
|
||||
'mention-user1' => [
|
||||
'type' => 'user',
|
||||
'id' => 'testUser',
|
||||
'name' => 'testUser display name',
|
||||
'mention-id' => 'testUser',
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals('Mention to {mention-user1} and {mention-user1} again', $chatMessage->getMessage());
|
||||
$this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters());
|
||||
}
|
||||
|
||||
public static function dataGetRichMessageWithMentionsFullyIncludedInOtherMentions(): array {
|
||||
// Based on valid characters from server/lib/private/User/Manager.php
|
||||
return [
|
||||
['testUser', 'testUser1', false],
|
||||
['testUser', 'testUser1', true],
|
||||
['testUser', 'testUser_1', false],
|
||||
['testUser', 'testUser_1', true],
|
||||
['testUser', 'testUser.1', false],
|
||||
['testUser', 'testUser.1', true],
|
||||
['testUser', 'testUser@1', false],
|
||||
['testUser', 'testUser@1', true],
|
||||
['testUser', 'testUser-1', false],
|
||||
['testUser', 'testUser-1', true],
|
||||
['testUser', 'testUser\'1', false],
|
||||
['testUser', 'testUser\'1', true],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataGetRichMessageWithMentionsFullyIncludedInOtherMentions')]
|
||||
public function testGetRichMessageWithMentionsFullyIncludedInOtherMentions(string $baseId, string $longerId, bool $quoted): void {
|
||||
$mentions = [
|
||||
['type' => 'user', 'id' => $baseId],
|
||||
['type' => 'user', 'id' => $longerId],
|
||||
];
|
||||
$comment = $this->newComment($mentions);
|
||||
|
||||
$this->commentsManager->expects($this->exactly(2))
|
||||
->method('resolveDisplayName')
|
||||
->willReturnCallback(function ($type, $id) {
|
||||
return $id . ' display name';
|
||||
});
|
||||
|
||||
$this->userManager->expects($this->exactly(2))
|
||||
->method('getDisplayName')
|
||||
->willReturnMap([
|
||||
[$longerId, $longerId . ' display name'],
|
||||
[$baseId, $baseId . ' display name']
|
||||
]);
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
if ($quoted) {
|
||||
$chatMessage->setMessage('Mention to @"' . $baseId . '" and @"' . $longerId . '"', []);
|
||||
} else {
|
||||
$chatMessage->setMessage('Mention to @' . $baseId . ' and @' . $longerId, []);
|
||||
}
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$expectedMessageParameters = [
|
||||
'mention-user1' => [
|
||||
'type' => 'user',
|
||||
'id' => $longerId,
|
||||
'name' => $longerId . ' display name',
|
||||
'mention-id' => $longerId,
|
||||
],
|
||||
'mention-user2' => [
|
||||
'type' => 'user',
|
||||
'id' => $baseId,
|
||||
'name' => $baseId . ' display name',
|
||||
'mention-id' => $baseId,
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals('Mention to {mention-user2} and {mention-user1}', $chatMessage->getMessage());
|
||||
$this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters());
|
||||
}
|
||||
|
||||
public function testGetRichMessageWithSeveralMentions(): void {
|
||||
$mentions = [
|
||||
['type' => 'user', 'id' => 'testUser1'],
|
||||
['type' => 'user', 'id' => 'testUser2'],
|
||||
['type' => 'user', 'id' => 'testUser3']
|
||||
];
|
||||
$comment = $this->newComment($mentions);
|
||||
|
||||
$this->commentsManager->expects($this->exactly(3))
|
||||
->method('resolveDisplayName')
|
||||
->willReturnMap([
|
||||
['user', 'testUser1', 'testUser1 display name'],
|
||||
['user', 'testUser2', 'testUser2 display name'],
|
||||
['user', 'testUser3', 'testUser3 display name'],
|
||||
]);
|
||||
|
||||
$this->userManager->expects($this->exactly(3))
|
||||
->method('getDisplayName')
|
||||
->willReturnMap([
|
||||
['testUser1', 'testUser1 display name'],
|
||||
['testUser2', 'testUser2 display name'],
|
||||
['testUser3', 'testUser3 display name'],
|
||||
]);
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
$chatMessage->setMessage('Mention to @testUser1, @testUser2, @testUser1 again and @testUser3', []);
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$expectedMessageParameters = [
|
||||
'mention-user1' => [
|
||||
'type' => 'user',
|
||||
'id' => 'testUser1',
|
||||
'name' => 'testUser1 display name',
|
||||
'mention-id' => 'testUser1',
|
||||
],
|
||||
'mention-user2' => [
|
||||
'type' => 'user',
|
||||
'id' => 'testUser2',
|
||||
'name' => 'testUser2 display name',
|
||||
'mention-id' => 'testUser2',
|
||||
],
|
||||
'mention-user3' => [
|
||||
'type' => 'user',
|
||||
'id' => 'testUser3',
|
||||
'name' => 'testUser3 display name',
|
||||
'mention-id' => 'testUser3',
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals('Mention to {mention-user1}, {mention-user2}, {mention-user1} again and {mention-user3}', $chatMessage->getMessage());
|
||||
$this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters());
|
||||
}
|
||||
|
||||
public function testGetRichMessageWithNonExistingUserMention(): void {
|
||||
$mentions = [
|
||||
['type' => 'user', 'id' => 'me'],
|
||||
['type' => 'user', 'id' => 'testUser'],
|
||||
];
|
||||
$comment = $this->newComment($mentions);
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('resolveDisplayName')
|
||||
->with('user', 'testUser')
|
||||
->willReturn('testUser display name');
|
||||
|
||||
$this->userManager->expects($this->exactly(2))
|
||||
->method('getDisplayName')
|
||||
->willReturnMap([
|
||||
['me', null],
|
||||
['testUser', 'testUser display name'],
|
||||
]);
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
$chatMessage->setMessage('Mention @me to @testUser', []);
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$expectedMessageParameters = [
|
||||
'mention-user1' => [
|
||||
'type' => 'user',
|
||||
'id' => 'testUser',
|
||||
'name' => 'testUser display name',
|
||||
'mention-id' => 'testUser',
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals('Mention @me to {mention-user1}', $chatMessage->getMessage());
|
||||
$this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters());
|
||||
}
|
||||
|
||||
public function testGetRichMessageWhenDisplayNameCanNotBeResolved(): void {
|
||||
$mentions = [
|
||||
['type' => 'user', 'id' => 'testUser'],
|
||||
];
|
||||
$comment = $this->newComment($mentions);
|
||||
|
||||
$this->commentsManager->expects($this->once())
|
||||
->method('resolveDisplayName')
|
||||
->willThrowException(new \OutOfBoundsException());
|
||||
|
||||
$this->userManager->expects($this->once())
|
||||
->method('getDisplayName')
|
||||
->with('testUser')
|
||||
->willReturn('existing user but does not resolve later');
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
$chatMessage->setMessage('Mention to @testUser', []);
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$expectedMessageParameters = [
|
||||
'mention-user1' => [
|
||||
'type' => 'user',
|
||||
'id' => 'testUser',
|
||||
'name' => '',
|
||||
'mention-id' => 'testUser',
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals('Mention to {mention-user1}', $chatMessage->getMessage());
|
||||
$this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters());
|
||||
}
|
||||
|
||||
public function testGetRichMessageWithAtAll(): void {
|
||||
$mentions = [
|
||||
['type' => 'user', 'id' => 'all'],
|
||||
];
|
||||
$metadata = [
|
||||
Message::METADATA_CAN_MENTION_ALL => true,
|
||||
];
|
||||
$comment = $this->newComment($mentions, $metadata);
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->expects($this->once())
|
||||
->method('getType')
|
||||
->willReturn(Room::TYPE_GROUP);
|
||||
$room->expects($this->once())
|
||||
->method('getToken')
|
||||
->willReturn('token');
|
||||
$room->expects($this->once())
|
||||
->method('getDisplayName')
|
||||
->willReturn('name');
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
$chatMessage->setMessage('Mention to @all', []);
|
||||
|
||||
$this->avatarService->method('getAvatarUrl')
|
||||
->with($room)
|
||||
->willReturn('getAvatarUrl');
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$expectedMessageParameters = [
|
||||
'mention-call1' => [
|
||||
'type' => 'call',
|
||||
'id' => 'token',
|
||||
'name' => 'name',
|
||||
'call-type' => 'group',
|
||||
'icon-url' => 'getAvatarUrl',
|
||||
'mention-id' => 'all',
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals('Mention to {mention-call1}', $chatMessage->getMessage());
|
||||
$this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters());
|
||||
}
|
||||
|
||||
public function testGetRichMessageWithFederatedUserMention(): void {
|
||||
$mentions = [
|
||||
['type' => 'federated_user', 'id' => 'testUser@example.tld'],
|
||||
];
|
||||
$comment = $this->newComment($mentions);
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
$chatMessage->setMessage('Mention to @"federated_user/testUser@example.tld"', []);
|
||||
|
||||
$cloudId = $this->createMock(ICloudId::class);
|
||||
$cloudId->method('getUser')
|
||||
->willReturn('testUser');
|
||||
$cloudId->method('getRemote')
|
||||
->willReturn('example.tld');
|
||||
$cloudId->method('getDisplayId')
|
||||
->willReturn('Display Id');
|
||||
$this->cloudIdManager->method('resolveCloudId')
|
||||
->with('testUser@example.tld')
|
||||
->willReturn($cloudId);
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$expectedMessageParameters = [
|
||||
'mention-federated-user1' => [
|
||||
'type' => 'user',
|
||||
'id' => 'testUser',
|
||||
'name' => 'Display Id',
|
||||
'server' => 'example.tld',
|
||||
'mention-id' => 'federated_user/testUser@example.tld',
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals('Mention to {mention-federated-user1}', $chatMessage->getMessage());
|
||||
$this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters());
|
||||
}
|
||||
|
||||
public function testGetRichMessageWhenAGuestWithoutNameIsMentioned(): void {
|
||||
$mentions = [
|
||||
['type' => 'guest', 'id' => 'guest/123456'],
|
||||
];
|
||||
$comment = $this->newComment($mentions);
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
|
||||
$this->participantService->method('getParticipantByActor')
|
||||
->with($room, Attendee::ACTOR_GUESTS, '123456')
|
||||
->willThrowException(new ParticipantNotFoundException());
|
||||
$this->l->expects($this->any())
|
||||
->method('t')
|
||||
->willReturnCallback(function ($text, $parameters = []) {
|
||||
return vsprintf($text, $parameters);
|
||||
});
|
||||
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
$chatMessage->setMessage('Mention to @"guest/123456"', []);
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$expectedMessageParameters = [
|
||||
'mention-guest1' => [
|
||||
'type' => 'guest',
|
||||
'id' => 'guest/123456',
|
||||
'name' => 'Guest',
|
||||
'mention-id' => 'guest/123456',
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals('Mention to {mention-guest1}', $chatMessage->getMessage());
|
||||
$this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters());
|
||||
}
|
||||
|
||||
public function testGetRichMessageWhenAGuestWithoutNameIsMentionedMultipleTimes(): void {
|
||||
$mentions = [
|
||||
['type' => 'guest', 'id' => 'guest/123456'],
|
||||
];
|
||||
$comment = $this->newComment($mentions);
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
|
||||
$this->participantService->method('getParticipantByActor')
|
||||
->with($room, Attendee::ACTOR_GUESTS, '123456')
|
||||
->willThrowException(new ParticipantNotFoundException());
|
||||
$this->l->expects($this->any())
|
||||
->method('t')
|
||||
->willReturnCallback(function ($text, $parameters = []) {
|
||||
return vsprintf($text, $parameters);
|
||||
});
|
||||
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
$chatMessage->setMessage('Mention to @"guest/123456", and again @"guest/123456"', []);
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$expectedMessageParameters = [
|
||||
'mention-guest1' => [
|
||||
'type' => 'guest',
|
||||
'id' => 'guest/123456',
|
||||
'name' => 'Guest',
|
||||
'mention-id' => 'guest/123456',
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals('Mention to {mention-guest1}, and again {mention-guest1}', $chatMessage->getMessage());
|
||||
$this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters());
|
||||
}
|
||||
|
||||
public function testGetRichMessageWhenAGuestWithANameIsMentionedMultipleTimes(): void {
|
||||
$mentions = [
|
||||
['type' => 'guest', 'id' => 'guest/abcdef'],
|
||||
];
|
||||
$comment = $this->newComment($mentions);
|
||||
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
/** @var Participant&MockObject $participant */
|
||||
$participant = $this->createMock(Participant::class);
|
||||
/** @var IL10N&MockObject $l */
|
||||
$l = $this->createMock(IL10N::class);
|
||||
|
||||
$attendee = Attendee::fromRow([
|
||||
'actor_type' => 'guests',
|
||||
'actor_id' => 'abcdef',
|
||||
'display_name' => 'Name',
|
||||
]);
|
||||
$participant->method('getAttendee')
|
||||
->willReturn($attendee);
|
||||
|
||||
$this->participantService->method('getParticipantByActor')
|
||||
->with($room, Attendee::ACTOR_GUESTS, 'abcdef')
|
||||
->willReturn($participant);
|
||||
$this->l->expects($this->any())
|
||||
->method('t')
|
||||
->willReturnCallback(function ($text, $parameters = []) {
|
||||
return vsprintf($text, $parameters);
|
||||
});
|
||||
|
||||
$chatMessage = new Message($room, $participant, $comment, $l);
|
||||
$chatMessage->setMessage('Mention to @"guest/abcdef", and again @"guest/abcdef"', []);
|
||||
|
||||
self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]);
|
||||
|
||||
$expectedMessageParameters = [
|
||||
'mention-guest1' => [
|
||||
'type' => 'guest',
|
||||
'id' => 'guest/abcdef',
|
||||
'name' => 'Name',
|
||||
'mention-id' => 'guest/abcdef',
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals('Mention to {mention-guest1}, and again {mention-guest1}', $chatMessage->getMessage());
|
||||
$this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,644 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Chat\SystemMessage;
|
||||
|
||||
use OCA\Talk\Chat\ChatManager;
|
||||
use OCA\Talk\Chat\MessageParser;
|
||||
use OCA\Talk\Chat\SystemMessage\Listener;
|
||||
use OCA\Talk\Events\AParticipantModifiedEvent;
|
||||
use OCA\Talk\Events\ARoomModifiedEvent;
|
||||
use OCA\Talk\Events\AttendeesAddedEvent;
|
||||
use OCA\Talk\Events\ParticipantModifiedEvent;
|
||||
use OCA\Talk\Events\RoomModifiedEvent;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\ThreadService;
|
||||
use OCA\Talk\TalkSession;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Comments\IComment;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IL10N;
|
||||
use OCP\IRequest;
|
||||
use OCP\ISession;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserSession;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class ListenerTest extends TestCase {
|
||||
public const DUMMY_REFERENCE_ID = 'DUMMY_REFERENCE_ID';
|
||||
|
||||
protected IRequest&MockObject $request;
|
||||
protected ChatManager&MockObject $chatManager;
|
||||
protected IUserSession&MockObject $userSession;
|
||||
protected ISession&MockObject $session;
|
||||
protected TalkSession&MockObject $talkSession;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected IEventDispatcher&MockObject $eventDispatcher;
|
||||
protected Manager&MockObject $manager;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected MessageParser&MockObject $messageParser;
|
||||
protected ThreadService&MockObject $threadService;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
protected ?array $handlers = null;
|
||||
protected ?\DateTime $dummyTime = null;
|
||||
protected ?Listener $listener = null;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->request = $this->createMock(IRequest::class);
|
||||
$this->request->expects($this->any())
|
||||
->method('getParam')
|
||||
->with('referenceId')
|
||||
->willReturn(self::DUMMY_REFERENCE_ID);
|
||||
|
||||
$this->dummyTime = new \DateTime();
|
||||
|
||||
$this->chatManager = $this->createMock(ChatManager::class);
|
||||
$this->session = $this->createMock(ISession::class);
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->talkSession = $this->createMock(TalkSession::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->timeFactory->method('getDateTime')->willReturn($this->dummyTime);
|
||||
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
|
||||
$this->manager = $this->createMock(Manager::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->messageParser = $this->createMock(MessageParser::class);
|
||||
$this->threadService = $this->createMock(ThreadService::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$l = $this->createMock(IL10N::class);
|
||||
$l->expects($this->any())
|
||||
->method('t')
|
||||
->willReturnCallback(function ($string, $args) {
|
||||
return vsprintf($string, $args);
|
||||
});
|
||||
|
||||
$this->handlers = [];
|
||||
|
||||
$this->eventDispatcher->method('addListener')
|
||||
->willReturnCallback(function ($eventName, $handler): void {
|
||||
$this->handlers[$eventName] ??= [];
|
||||
$this->handlers[$eventName][] = $handler;
|
||||
});
|
||||
|
||||
$this->listener = new Listener(
|
||||
$this->request,
|
||||
$this->chatManager,
|
||||
$this->talkSession,
|
||||
$this->session,
|
||||
$this->userSession,
|
||||
$this->timeFactory,
|
||||
$this->manager,
|
||||
$this->participantService,
|
||||
$this->messageParser,
|
||||
$this->threadService,
|
||||
$l,
|
||||
$this->logger,
|
||||
);
|
||||
}
|
||||
|
||||
private function dispatch(string $eventName, $event): void {
|
||||
$handlers = $this->handlers[$eventName];
|
||||
$this->assertCount(1, $handlers);
|
||||
|
||||
$handlers[0]($event);
|
||||
}
|
||||
|
||||
private function mockLoggedInUser($userId): IUser {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')->willReturn($userId);
|
||||
$this->userSession
|
||||
->method('getUser')
|
||||
->willReturn($user);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function testAfterUsersAddOneToOne(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->expects($this->any())
|
||||
->method('getType')
|
||||
->willReturn(Room::TYPE_ONE_TO_ONE);
|
||||
|
||||
$participants = [[
|
||||
'actorType' => 'users',
|
||||
'actorId' => 'alice_actor',
|
||||
'participantType' => Participant::USER,
|
||||
]];
|
||||
$attendees = array_map(static fn (array $participant) => Attendee::fromParams($participant), $participants);
|
||||
$event = new AttendeesAddedEvent($room, $attendees);
|
||||
|
||||
$this->chatManager->expects($this->never())
|
||||
->method('addSystemMessage');
|
||||
|
||||
self::invokePrivate($this->listener, 'handle', [$event]);
|
||||
}
|
||||
|
||||
public static function dataRoomTypes(): array {
|
||||
$expectedMessages = [
|
||||
[
|
||||
'actorType' => 'users',
|
||||
'actorId' => 'alice_actor',
|
||||
'message' => ['message' => 'user_added', 'parameters' => ['user' => 'alice_actor']],
|
||||
],
|
||||
[
|
||||
'actorType' => 'users',
|
||||
'actorId' => 'alice_actor',
|
||||
'message' => ['message' => 'user_added', 'parameters' => ['user' => 'bob']],
|
||||
],
|
||||
[
|
||||
'actorType' => 'users',
|
||||
'actorId' => 'alice_actor',
|
||||
'message' => ['message' => 'user_added', 'parameters' => ['user' => 'carmen']],
|
||||
],
|
||||
[
|
||||
'actorType' => 'users',
|
||||
'actorId' => 'alice_actor',
|
||||
'message' => ['message' => 'user_added', 'parameters' => ['user' => 'delta']],
|
||||
],
|
||||
];
|
||||
|
||||
$allParticipants = [
|
||||
// guest will be ignored
|
||||
[
|
||||
'actorType' => 'guests'
|
||||
],
|
||||
// alice_actor adding self to listed channel
|
||||
[
|
||||
'actorType' => 'users',
|
||||
'actorId' => 'alice_actor',
|
||||
'participantType' => Participant::USER,
|
||||
],
|
||||
// alice_actor added bob
|
||||
[
|
||||
'actorType' => 'users',
|
||||
'actorId' => 'bob',
|
||||
'participantType' => Participant::USER,
|
||||
],
|
||||
// empty participant type
|
||||
[
|
||||
'actorType' => 'users',
|
||||
'actorId' => 'carmen',
|
||||
],
|
||||
// alice_actor adding self-joined mode
|
||||
[
|
||||
'actorType' => 'users',
|
||||
'actorId' => 'delta',
|
||||
'participantType' => Participant::USER_SELF_JOINED,
|
||||
],
|
||||
];
|
||||
|
||||
return [
|
||||
[Room::TYPE_GROUP, '', $allParticipants, $expectedMessages],
|
||||
[Room::TYPE_PUBLIC, '', $allParticipants, $expectedMessages],
|
||||
[Room::TYPE_ONE_TO_ONE, '', $allParticipants, []],
|
||||
[Room::TYPE_GROUP, 'file', $allParticipants, $expectedMessages],
|
||||
[Room::TYPE_PUBLIC, 'file', $allParticipants, $expectedMessages],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataRoomTypes')]
|
||||
public function testAfterUsersAdd(int $roomType, string $objectType, array $participants, array $expectedMessages): void {
|
||||
$this->mockLoggedInUser('alice_actor');
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getType')->willReturn($roomType);
|
||||
$room->method('getObjectType')->willReturn($objectType);
|
||||
|
||||
$attendees = array_map(static fn (array $participant) => Attendee::fromParams($participant), $participants);
|
||||
|
||||
// TODO: add all cases
|
||||
$event = new AttendeesAddedEvent($room, $attendees);
|
||||
|
||||
$consecutive = [];
|
||||
foreach ($expectedMessages as $expectedMessage) {
|
||||
$consecutive[] = [
|
||||
$room,
|
||||
null,
|
||||
$expectedMessage['actorType'],
|
||||
$expectedMessage['actorId'],
|
||||
json_encode($expectedMessage['message']),
|
||||
$this->dummyTime,
|
||||
false,
|
||||
self::DUMMY_REFERENCE_ID,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
];
|
||||
}
|
||||
if (!empty($consecutive)) {
|
||||
$i = 0;
|
||||
$this->chatManager->expects($this->exactly(count($consecutive)))
|
||||
->method('addSystemMessage')
|
||||
->willReturnCallback(function () use ($consecutive, &$i) {
|
||||
$this->assertArrayHasKey($i, $consecutive);
|
||||
$this->assertSame($consecutive[$i], func_get_args());
|
||||
$i++;
|
||||
return $this->createMock(IComment::class);
|
||||
});
|
||||
} else {
|
||||
$this->chatManager->expects($this->never())
|
||||
->method('addSystemMessage');
|
||||
}
|
||||
|
||||
self::invokePrivate($this->listener, 'handle', [$event]);
|
||||
}
|
||||
|
||||
public static function dataParticipantTypeChange(): array {
|
||||
return [
|
||||
[
|
||||
Attendee::ACTOR_GROUPS,
|
||||
Participant::USER,
|
||||
Participant::MODERATOR,
|
||||
[],
|
||||
],
|
||||
[
|
||||
Attendee::ACTOR_USERS,
|
||||
Participant::USER,
|
||||
Participant::MODERATOR,
|
||||
[['message' => 'moderator_promoted', 'parameters' => ['user' => 'bob_participant']]],
|
||||
],
|
||||
[
|
||||
Attendee::ACTOR_USERS,
|
||||
Participant::MODERATOR,
|
||||
Participant::USER,
|
||||
[['message' => 'moderator_demoted', 'parameters' => ['user' => 'bob_participant']]],
|
||||
],
|
||||
[
|
||||
Attendee::ACTOR_GUESTS,
|
||||
Participant::GUEST,
|
||||
Participant::GUEST_MODERATOR,
|
||||
[['message' => 'guest_moderator_promoted', 'parameters' => ['type' => 'guests', 'id' => 'bob_participant']]],
|
||||
],
|
||||
[
|
||||
Attendee::ACTOR_GUESTS,
|
||||
Participant::GUEST_MODERATOR,
|
||||
Participant::GUEST,
|
||||
[['message' => 'guest_moderator_demoted', 'parameters' => ['type' => 'guests', 'id' => 'bob_participant']]],
|
||||
],
|
||||
[
|
||||
Attendee::ACTOR_EMAILS,
|
||||
Participant::GUEST,
|
||||
Participant::GUEST_MODERATOR,
|
||||
[['message' => 'guest_moderator_promoted', 'parameters' => ['type' => 'emails', 'id' => 'bob_participant']]],
|
||||
],
|
||||
[
|
||||
Attendee::ACTOR_EMAILS,
|
||||
Participant::GUEST_MODERATOR,
|
||||
Participant::GUEST,
|
||||
[['message' => 'guest_moderator_demoted', 'parameters' => ['type' => 'emails', 'id' => 'bob_participant']]],
|
||||
],
|
||||
[
|
||||
Attendee::ACTOR_USERS,
|
||||
Participant::USER_SELF_JOINED,
|
||||
Participant::USER,
|
||||
[['message' => 'user_added', 'parameters' => ['user' => 'bob_participant']]],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataParticipantTypeChange')]
|
||||
public function testAfterParticipantTypeSet(string $actorType, int $oldParticipantType, int $newParticipantType, array $expectedMessages): void {
|
||||
$this->mockLoggedInUser('alice_actor');
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getType')->willReturn(Room::TYPE_GROUP);
|
||||
|
||||
$attendee = new Attendee();
|
||||
$attendee->setActorId('bob_participant');
|
||||
$attendee->setActorType($actorType);
|
||||
|
||||
$participant = $this->createMock(Participant::class);
|
||||
$participant->method('getAttendee')->willReturn($attendee);
|
||||
|
||||
$event = new ParticipantModifiedEvent($room, $participant, AParticipantModifiedEvent::PROPERTY_TYPE, $newParticipantType, $oldParticipantType);
|
||||
|
||||
foreach ($expectedMessages as $expectedMessage) {
|
||||
$consecutive[] = [
|
||||
$room,
|
||||
null,
|
||||
Attendee::ACTOR_USERS,
|
||||
'alice_actor',
|
||||
json_encode($expectedMessage),
|
||||
$this->dummyTime,
|
||||
false,
|
||||
self::DUMMY_REFERENCE_ID,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
];
|
||||
}
|
||||
if (isset($consecutive)) {
|
||||
$i = 0;
|
||||
$this->chatManager->expects($this->exactly(count($consecutive)))
|
||||
->method('addSystemMessage')
|
||||
->willReturnCallback(function () use ($consecutive, &$i) {
|
||||
$this->assertArrayHasKey($i, $consecutive);
|
||||
$this->assertSame($consecutive[$i], func_get_args());
|
||||
$i++;
|
||||
return $this->createMock(IComment::class);
|
||||
});
|
||||
} else {
|
||||
$this->chatManager->expects($this->never())
|
||||
->method('addSystemMessage');
|
||||
}
|
||||
|
||||
self::invokePrivate($this->listener, 'handle', [$event]);
|
||||
}
|
||||
|
||||
public static function dataCallRecordingChange(): array {
|
||||
return [
|
||||
[
|
||||
Room::RECORDING_VIDEO_STARTING,
|
||||
Room::RECORDING_NONE,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_VIDEO_STARTING,
|
||||
Room::RECORDING_NONE,
|
||||
Attendee::ACTOR_USERS,
|
||||
'alice',
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_VIDEO,
|
||||
Room::RECORDING_VIDEO_STARTING,
|
||||
null,
|
||||
null,
|
||||
['message' => 'recording_started', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_VIDEO,
|
||||
Room::RECORDING_VIDEO_STARTING,
|
||||
Attendee::ACTOR_USERS,
|
||||
'alice',
|
||||
['message' => 'recording_started', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_VIDEO,
|
||||
Room::RECORDING_NONE,
|
||||
null,
|
||||
null,
|
||||
['message' => 'recording_started', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_VIDEO,
|
||||
Room::RECORDING_NONE,
|
||||
Attendee::ACTOR_USERS,
|
||||
'alice',
|
||||
['message' => 'recording_started', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_AUDIO_STARTING,
|
||||
Room::RECORDING_NONE,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_AUDIO_STARTING,
|
||||
Room::RECORDING_NONE,
|
||||
Attendee::ACTOR_USERS,
|
||||
'alice',
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_AUDIO,
|
||||
Room::RECORDING_AUDIO_STARTING,
|
||||
null,
|
||||
null,
|
||||
['message' => 'audio_recording_started', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_AUDIO,
|
||||
Room::RECORDING_AUDIO_STARTING,
|
||||
Attendee::ACTOR_USERS,
|
||||
'alice',
|
||||
['message' => 'audio_recording_started', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_AUDIO,
|
||||
Room::RECORDING_NONE,
|
||||
null,
|
||||
null,
|
||||
['message' => 'audio_recording_started', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_AUDIO,
|
||||
Room::RECORDING_NONE,
|
||||
Attendee::ACTOR_USERS,
|
||||
'alice',
|
||||
['message' => 'audio_recording_started', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_NONE,
|
||||
Room::RECORDING_VIDEO_STARTING,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_NONE,
|
||||
Room::RECORDING_VIDEO_STARTING,
|
||||
Attendee::ACTOR_USERS,
|
||||
'bob',
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_NONE,
|
||||
Room::RECORDING_VIDEO,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_NONE,
|
||||
Room::RECORDING_VIDEO,
|
||||
Attendee::ACTOR_USERS,
|
||||
'bob',
|
||||
['message' => 'recording_stopped', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_NONE,
|
||||
Room::RECORDING_AUDIO_STARTING,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_NONE,
|
||||
Room::RECORDING_AUDIO_STARTING,
|
||||
Attendee::ACTOR_USERS,
|
||||
'bob',
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_NONE,
|
||||
Room::RECORDING_AUDIO,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_NONE,
|
||||
Room::RECORDING_AUDIO,
|
||||
Attendee::ACTOR_USERS,
|
||||
'bob',
|
||||
['message' => 'audio_recording_stopped', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_FAILED,
|
||||
Room::RECORDING_VIDEO_STARTING,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_FAILED,
|
||||
Room::RECORDING_AUDIO_STARTING,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_FAILED,
|
||||
Room::RECORDING_VIDEO,
|
||||
null,
|
||||
null,
|
||||
['message' => 'recording_failed', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_FAILED,
|
||||
Room::RECORDING_AUDIO,
|
||||
null,
|
||||
null,
|
||||
['message' => 'recording_failed', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_VIDEO_STARTING,
|
||||
Room::RECORDING_FAILED,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_VIDEO_STARTING,
|
||||
Room::RECORDING_FAILED,
|
||||
Attendee::ACTOR_USERS,
|
||||
'alice',
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_VIDEO,
|
||||
Room::RECORDING_FAILED,
|
||||
null,
|
||||
null,
|
||||
['message' => 'recording_started', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_VIDEO,
|
||||
Room::RECORDING_FAILED,
|
||||
Attendee::ACTOR_USERS,
|
||||
'alice',
|
||||
['message' => 'recording_started', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_AUDIO_STARTING,
|
||||
Room::RECORDING_FAILED,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_AUDIO_STARTING,
|
||||
Room::RECORDING_FAILED,
|
||||
Attendee::ACTOR_USERS,
|
||||
'alice',
|
||||
null,
|
||||
],
|
||||
[
|
||||
Room::RECORDING_AUDIO,
|
||||
Room::RECORDING_FAILED,
|
||||
null,
|
||||
null,
|
||||
['message' => 'audio_recording_started', 'parameters' => []],
|
||||
],
|
||||
[
|
||||
Room::RECORDING_AUDIO,
|
||||
Room::RECORDING_FAILED,
|
||||
Attendee::ACTOR_USERS,
|
||||
'alice',
|
||||
['message' => 'audio_recording_started', 'parameters' => []],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataCallRecordingChange')]
|
||||
public function testAfterCallRecordingSet(int $newStatus, int $oldStatus, ?string $actorType, ?string $actorId, ?array $expectedMessage): void {
|
||||
$this->mockLoggedInUser('logged_in_user');
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->expects($this->any())
|
||||
->method('getType')
|
||||
->willReturn(Room::TYPE_PUBLIC);
|
||||
|
||||
if ($actorType !== null && $actorId !== null) {
|
||||
$attendee = new Attendee();
|
||||
$attendee->setActorType($actorType);
|
||||
$attendee->setActorId($actorId);
|
||||
|
||||
$participant = $this->createMock(Participant::class);
|
||||
$participant->method('getAttendee')->willReturn($attendee);
|
||||
|
||||
$expectedActorType = $actorType;
|
||||
$expectedActorId = $actorId;
|
||||
} else {
|
||||
$participant = null;
|
||||
|
||||
$expectedActorType = Attendee::ACTOR_USERS;
|
||||
$expectedActorId = 'logged_in_user';
|
||||
}
|
||||
|
||||
$event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_CALL_RECORDING, $newStatus, $oldStatus, $participant);
|
||||
|
||||
if ($expectedMessage !== null) {
|
||||
$this->chatManager->expects($this->once())
|
||||
->method('addSystemMessage')
|
||||
->with(
|
||||
$room,
|
||||
$participant,
|
||||
$expectedActorType,
|
||||
$expectedActorId,
|
||||
json_encode($expectedMessage),
|
||||
$this->dummyTime,
|
||||
false,
|
||||
self::DUMMY_REFERENCE_ID,
|
||||
null,
|
||||
false
|
||||
);
|
||||
} else {
|
||||
$this->chatManager->expects($this->never())
|
||||
->method('addSystemMessage');
|
||||
}
|
||||
|
||||
self::invokePrivate($this->listener, 'handle', [$event]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Collaboration\Collaborators;
|
||||
|
||||
use OCA\Talk\Collaboration\Collaborators\RoomPlugin;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCP\Collaboration\Collaborators\ISearchResult;
|
||||
use OCP\Collaboration\Collaborators\SearchResultType;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserSession;
|
||||
use OCP\Share\IShare;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class RoomPluginTest extends TestCase {
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected ?Manager $manager = null;
|
||||
protected ?IUserSession $userSession = null;
|
||||
protected ?IUser $user = null;
|
||||
protected ?ISearchResult $searchResult = null;
|
||||
protected ?RoomPlugin $plugin = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->manager = $this->createMock(Manager::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
|
||||
$this->user = $this->createMock(IUser::class);
|
||||
$this->user->expects($this->any())
|
||||
->method('getUID')
|
||||
->willReturn('user0');
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->userSession->expects($this->any())
|
||||
->method('getUser')
|
||||
->willReturn($this->user);
|
||||
|
||||
$this->searchResult = $this->createMock(ISearchResult::class);
|
||||
|
||||
$this->plugin = new RoomPlugin(
|
||||
$this->manager,
|
||||
$this->participantService,
|
||||
$this->userSession
|
||||
);
|
||||
}
|
||||
|
||||
private function newRoom(int $type, string $token, string $name, int $permissions = Attendee::PERMISSIONS_MAX_DEFAULT): Room {
|
||||
$room = $this->createMock(Room::class);
|
||||
$participant = $this->createMock(Participant::class);
|
||||
|
||||
$room->expects($this->any())
|
||||
->method('getType')
|
||||
->willReturn($type);
|
||||
|
||||
$room->expects($this->any())
|
||||
->method('getToken')
|
||||
->willReturn($token);
|
||||
|
||||
$room->expects($this->any())
|
||||
->method('getDisplayName')
|
||||
->willReturn($name);
|
||||
|
||||
$this->participantService->expects($this->any())
|
||||
->method('getParticipant')
|
||||
->willReturn($participant);
|
||||
|
||||
$participant->expects($this->any())
|
||||
->method('getPermissions')
|
||||
->willReturn($permissions);
|
||||
|
||||
return $room;
|
||||
}
|
||||
|
||||
private static function newResult(string $label, string $shareWith): array {
|
||||
return [
|
||||
'label' => $label,
|
||||
'value' => [
|
||||
'shareType' => IShare::TYPE_ROOM,
|
||||
'shareWith' => $shareWith
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public static function dataSearch(): array {
|
||||
return [
|
||||
// Empty search term with no rooms
|
||||
['', 2, 0, [], [], [], false],
|
||||
|
||||
// Empty search term with rooms
|
||||
['', 2, 0, [
|
||||
[Room::TYPE_GROUP, 'roomToken', 'Room name'],
|
||||
], [], [], false],
|
||||
|
||||
// Search term with no matches
|
||||
['Unmatched search term', 2, 0, [
|
||||
[Room::TYPE_GROUP, 'roomToken', 'Unmatched name'],
|
||||
], [], [], false],
|
||||
|
||||
// Search term with single wide match
|
||||
['room', 2, 0, [
|
||||
[Room::TYPE_GROUP, 'roomToken', 'Room name'],
|
||||
[Room::TYPE_GROUP, 'roomToken2', 'Unmatched name'],
|
||||
], [], [
|
||||
self::newResult('Room name', 'roomToken'),
|
||||
], false],
|
||||
|
||||
// Chats without chat permission are not returned
|
||||
['room', 2, 0, [
|
||||
[Room::TYPE_GROUP, 'roomToken', 'Room name', Attendee::PERMISSIONS_MAX_DEFAULT ^ Attendee::PERMISSIONS_CHAT],
|
||||
], [], [], false],
|
||||
|
||||
// Search term with single exact match
|
||||
['room name', 2, 0, [
|
||||
[Room::TYPE_GROUP, 'roomToken', 'Unmatched name'],
|
||||
[Room::TYPE_GROUP, 'roomToken2', 'Room name'],
|
||||
], [
|
||||
self::newResult('Room name', 'roomToken2'),
|
||||
], [], false],
|
||||
|
||||
// Search term with single exact match and single wide match
|
||||
['room name', 2, 0, [
|
||||
[Room::TYPE_GROUP, 'roomToken', 'Room name that also matches'],
|
||||
[Room::TYPE_GROUP, 'roomToken2', 'Room name'],
|
||||
], [
|
||||
self::newResult('Room name', 'roomToken2'),
|
||||
], [
|
||||
self::newResult('Room name that also matches', 'roomToken'),
|
||||
], false],
|
||||
|
||||
// Search term matching one-to-one rooms (not possible in practice
|
||||
// as one-to-one rooms do not have a name, but it would be if they
|
||||
// had, so it is included here for completeness).
|
||||
['room name', 2, 0, [
|
||||
[Room::TYPE_ONE_TO_ONE, 'roomToken', 'Room name that also matches'],
|
||||
[Room::TYPE_ONE_TO_ONE, 'roomToken2', 'Room name'],
|
||||
], [
|
||||
self::newResult('Room name', 'roomToken2'),
|
||||
], [
|
||||
self::newResult('Room name that also matches', 'roomToken'),
|
||||
], false],
|
||||
|
||||
// Search term matching public rooms
|
||||
['room name', 2, 0, [
|
||||
[Room::TYPE_PUBLIC, 'roomToken', 'Room name that also matches'],
|
||||
[Room::TYPE_PUBLIC, 'roomToken2', 'Room name'],
|
||||
], [
|
||||
self::newResult('Room name', 'roomToken2'),
|
||||
], [
|
||||
self::newResult('Room name that also matches', 'roomToken'),
|
||||
], false],
|
||||
|
||||
// Search term with several wide matches
|
||||
['room', 2, 0, [
|
||||
[Room::TYPE_GROUP, 'roomToken', 'Room name'],
|
||||
[Room::TYPE_GROUP, 'roomToken2', 'Another room name'],
|
||||
[Room::TYPE_GROUP, 'roomToken3', 'Room name'],
|
||||
[Room::TYPE_GROUP, 'roomToken4', 'Another room name'],
|
||||
], [], [
|
||||
self::newResult('Room name', 'roomToken'),
|
||||
self::newResult('Another room name', 'roomToken2'),
|
||||
self::newResult('Room name', 'roomToken3'),
|
||||
self::newResult('Another room name', 'roomToken4'),
|
||||
], false],
|
||||
|
||||
// Search term with several exact matches
|
||||
['room name', 2, 0, [
|
||||
[Room::TYPE_GROUP, 'roomToken', 'Room name'],
|
||||
[Room::TYPE_GROUP, 'roomToken2', 'Room name'],
|
||||
[Room::TYPE_GROUP, 'roomToken3', 'Room name'],
|
||||
[Room::TYPE_GROUP, 'roomToken4', 'Room name'],
|
||||
], [
|
||||
self::newResult('Room name', 'roomToken'),
|
||||
self::newResult('Room name', 'roomToken2'),
|
||||
self::newResult('Room name', 'roomToken3'),
|
||||
self::newResult('Room name', 'roomToken4'),
|
||||
], [], false],
|
||||
|
||||
// Search term with several matches
|
||||
['room name', 2, 0, [
|
||||
[Room::TYPE_GROUP, 'roomToken', 'Room name'],
|
||||
[Room::TYPE_GROUP, 'roomToken2', 'Unmatched name'],
|
||||
[Room::TYPE_GROUP, 'roomToken3', 'Another room name'],
|
||||
[Room::TYPE_GROUP, 'roomToken4', 'Room name'],
|
||||
[Room::TYPE_ONE_TO_ONE, 'roomToken5', 'Room name'],
|
||||
[Room::TYPE_PUBLIC, 'roomToken6', 'Room name'],
|
||||
[Room::TYPE_GROUP, 'roomToken7', 'Another unmatched name'],
|
||||
[Room::TYPE_ONE_TO_ONE, 'roomToken8', 'Another unmatched name'],
|
||||
[Room::TYPE_PUBLIC, 'roomToken9', 'Another unmatched name'],
|
||||
[Room::TYPE_ONE_TO_ONE, 'roomToken10', 'Another room name'],
|
||||
[Room::TYPE_PUBLIC, 'roomToken11', 'Another room name'],
|
||||
], [
|
||||
self::newResult('Room name', 'roomToken'),
|
||||
self::newResult('Room name', 'roomToken4'),
|
||||
self::newResult('Room name', 'roomToken5'),
|
||||
self::newResult('Room name', 'roomToken6'),
|
||||
], [
|
||||
self::newResult('Another room name', 'roomToken3'),
|
||||
self::newResult('Another room name', 'roomToken10'),
|
||||
self::newResult('Another room name', 'roomToken11'),
|
||||
], false],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataSearch')]
|
||||
public function testSearch(
|
||||
string $searchTerm,
|
||||
int $limit,
|
||||
int $offset,
|
||||
array $roomsForParticipant,
|
||||
array $expectedMatchesExact,
|
||||
array $expectedMatches,
|
||||
bool $expectedHasMoreResults,
|
||||
) {
|
||||
$rooms = [];
|
||||
foreach ($roomsForParticipant as $roomData) {
|
||||
$rooms[] = call_user_func_array([$this, 'newRoom'], $roomData);
|
||||
}
|
||||
|
||||
$this->manager->expects($this->any())
|
||||
->method('getRoomsForUser')
|
||||
->with('user0')
|
||||
->willReturn($rooms);
|
||||
|
||||
$this->searchResult->expects($this->any())
|
||||
->method('addResultSet')
|
||||
->with(
|
||||
$this->callback(
|
||||
function (SearchResultType $searchResultType) {
|
||||
return $searchResultType->getLabel() === 'rooms';
|
||||
}
|
||||
),
|
||||
$expectedMatches,
|
||||
$expectedMatchesExact
|
||||
);
|
||||
|
||||
$hasMoreResults = $this->plugin->search($searchTerm, $limit, $offset, $this->searchResult);
|
||||
|
||||
$this->assertSame($expectedHasMoreResults, $hasMoreResults);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Collaboration\Resources;
|
||||
|
||||
use OCA\Talk\Chat\ChatManager;
|
||||
use OCA\Talk\Chat\MessageParser;
|
||||
use OCA\Talk\Collaboration\Reference\TalkReferenceProvider;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\ProxyCacheMessageMapper;
|
||||
use OCA\Talk\Service\AvatarService;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class TalkReferenceProviderTest extends TestCase {
|
||||
protected IURLGenerator&MockObject $urlGenerator;
|
||||
protected Manager&MockObject $roomManager;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected ChatManager&MockObject $chatManager;
|
||||
protected ProxyCacheMessageMapper&MockObject $proxyCacheMessageMapper;
|
||||
protected AvatarService&MockObject $avatarService;
|
||||
protected MessageParser&MockObject $messageParser;
|
||||
protected IL10N&MockObject $l;
|
||||
protected ?TalkReferenceProvider $provider = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
$this->roomManager = $this->createMock(Manager::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->chatManager = $this->createMock(ChatManager::class);
|
||||
$this->proxyCacheMessageMapper = $this->createMock(ProxyCacheMessageMapper::class);
|
||||
$this->avatarService = $this->createMock(AvatarService::class);
|
||||
$this->messageParser = $this->createMock(MessageParser::class);
|
||||
$this->l = $this->createMock(IL10N::class);
|
||||
|
||||
$this->provider = new TalkReferenceProvider(
|
||||
$this->urlGenerator,
|
||||
$this->roomManager,
|
||||
$this->participantService,
|
||||
$this->chatManager,
|
||||
$this->proxyCacheMessageMapper,
|
||||
$this->avatarService,
|
||||
$this->messageParser,
|
||||
$this->l,
|
||||
'test'
|
||||
);
|
||||
}
|
||||
|
||||
public static function dataGetTalkAppLinkToken(): array {
|
||||
return [
|
||||
['https://localhost/', null],
|
||||
['https://localhost/call', null],
|
||||
['https://localhost/call/abcdef', ['token' => 'abcdef', 'message' => null]],
|
||||
['https://localhost/call/abcdef?query=1', ['token' => 'abcdef', 'message' => null]],
|
||||
['https://localhost/call/abcdef#hash=1', ['token' => 'abcdef', 'message' => null]],
|
||||
['https://localhost/call/abcdef#message_123', ['token' => 'abcdef', 'message' => 123]],
|
||||
['https://localhost/call/abcdef?query=1#message_123', ['token' => 'abcdef', 'message' => 123]],
|
||||
['https://localhost/call/abcdef?query=1#message_123bcd', ['token' => 'abcdef', 'message' => null]],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataGetTalkAppLinkToken')]
|
||||
public function testGetTalkAppLinkToken(string $reference, ?array $expected): void {
|
||||
$this->urlGenerator->expects($this->any())
|
||||
->method('getAbsoluteURL')
|
||||
->willReturnCallback(static fn ($url) => 'https://localhost' . $url);
|
||||
|
||||
$actual = self::invokePrivate($this->provider, 'getTalkAppLinkToken', [$reference]);
|
||||
self::assertSame($expected, $actual);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Collaboration\Resources;
|
||||
|
||||
use OCA\Talk\Collaboration\Resources\ConversationProvider;
|
||||
use OCA\Talk\Exceptions\ParticipantNotFoundException;
|
||||
use OCA\Talk\Exceptions\RoomNotFoundException;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\AvatarService;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCP\Collaboration\Resources\IResource;
|
||||
use OCP\Collaboration\Resources\ResourceException;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserSession;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class ConversationProviderTest extends TestCase {
|
||||
protected Manager&MockObject $manager;
|
||||
protected AvatarService&MockObject $avatarService;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected IUserSession&MockObject $userSession;
|
||||
protected IURLGenerator&MockObject $urlGenerator;
|
||||
protected ?ConversationProvider $provider = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->manager = $this->createMock(Manager::class);
|
||||
$this->avatarService = $this->createMock(AvatarService::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
|
||||
$this->provider = new ConversationProvider(
|
||||
$this->manager,
|
||||
$this->avatarService,
|
||||
$this->participantService,
|
||||
$this->userSession,
|
||||
$this->urlGenerator
|
||||
);
|
||||
}
|
||||
|
||||
public function testCanAccessResourceThrowsGuest(): void {
|
||||
$resource = $this->createMock(IResource::class);
|
||||
|
||||
$this->expectException(ResourceException::class);
|
||||
$this->expectExceptionMessage('Guests are not supported at the moment');
|
||||
$this->provider->canAccessResource($resource, null);
|
||||
}
|
||||
|
||||
public function testCanAccessResourceThrowsRoom(): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->expects($this->once())
|
||||
->method('getUID')
|
||||
->willReturn('uid');
|
||||
$resource = $this->createMock(IResource::class);
|
||||
$resource->expects($this->once())
|
||||
->method('getId')
|
||||
->willReturn('token');
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('getRoomForUserByToken')
|
||||
->with('token', 'uid')
|
||||
->willThrowException(new RoomNotFoundException());
|
||||
|
||||
$this->expectExceptionMessage('Conversation not found');
|
||||
$this->provider->canAccessResource($resource, $user);
|
||||
}
|
||||
|
||||
public function testCanAccessResourceThrowsParticipant(): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->expects($this->once())
|
||||
->method('getUID')
|
||||
->willReturn('uid');
|
||||
$resource = $this->createMock(IResource::class);
|
||||
$resource->expects($this->once())
|
||||
->method('getId')
|
||||
->willReturn('token');
|
||||
$room = $this->createMock(Room::class);
|
||||
$this->participantService->expects($this->once())
|
||||
->method('getParticipant')
|
||||
->with($room, 'uid')
|
||||
->willThrowException(new ParticipantNotFoundException());
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('getRoomForUserByToken')
|
||||
->with('token', 'uid')
|
||||
->willReturn($room);
|
||||
|
||||
$this->expectExceptionMessage('Participant not found');
|
||||
$this->provider->canAccessResource($resource, $user);
|
||||
}
|
||||
|
||||
public function testCanAccessResourceParticipantNotAdded(): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->expects($this->once())
|
||||
->method('getUID')
|
||||
->willReturn('uid');
|
||||
$resource = $this->createMock(IResource::class);
|
||||
$resource->expects($this->once())
|
||||
->method('getId')
|
||||
->willReturn('token');
|
||||
|
||||
$participant = $this->createMock(Participant::class);
|
||||
$attendee = Attendee::fromRow([
|
||||
'actor_type' => 'users',
|
||||
'actor_id' => 'uid',
|
||||
'participant_type' => Participant::USER_SELF_JOINED,
|
||||
]);
|
||||
$participant->expects($this->any())
|
||||
->method('getAttendee')
|
||||
->willReturn($attendee);
|
||||
$room = $this->createMock(Room::class);
|
||||
$this->participantService->expects($this->once())
|
||||
->method('getParticipant')
|
||||
->with($room, 'uid')
|
||||
->willReturn($participant);
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('getRoomForUserByToken')
|
||||
->with('token', 'uid')
|
||||
->willReturn($room);
|
||||
|
||||
$this->assertFalse($this->provider->canAccessResource($resource, $user));
|
||||
}
|
||||
|
||||
public static function dataCanAccessResourceYes(): array {
|
||||
return [
|
||||
[Participant::OWNER],
|
||||
[Participant::MODERATOR],
|
||||
[Participant::USER],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataCanAccessResourceYes')]
|
||||
public function testCanAccessResourceYes(int $participantType): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->expects($this->once())
|
||||
->method('getUID')
|
||||
->willReturn('uid');
|
||||
$resource = $this->createMock(IResource::class);
|
||||
$resource->expects($this->once())
|
||||
->method('getId')
|
||||
->willReturn('token');
|
||||
|
||||
$participant = $this->createMock(Participant::class);
|
||||
$attendee = Attendee::fromRow([
|
||||
'actor_type' => 'users',
|
||||
'actor_id' => 'uid',
|
||||
'participant_type' => $participantType,
|
||||
]);
|
||||
$participant->expects($this->any())
|
||||
->method('getAttendee')
|
||||
->willReturn($attendee);
|
||||
$room = $this->createMock(Room::class);
|
||||
$this->participantService->expects($this->once())
|
||||
->method('getParticipant')
|
||||
->with($room, 'uid')
|
||||
->willReturn($participant);
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('getRoomForUserByToken')
|
||||
->with('token', 'uid')
|
||||
->willReturn($room);
|
||||
|
||||
$this->assertTrue($this->provider->canAccessResource($resource, $user));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Talk\Tests\php\Command\Signaling;
|
||||
|
||||
use OCA\Talk\Command\Signaling\Add;
|
||||
use OCP\IConfig;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class AddTest extends TestCase {
|
||||
protected IConfig&MockObject $config;
|
||||
protected InputInterface&MockObject $input;
|
||||
protected OutputInterface&MockObject $output;
|
||||
protected Add $command;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
|
||||
$this->command = new Add($this->config);
|
||||
|
||||
$this->input = $this->createMock(InputInterface::class);
|
||||
$this->output = $this->createMock(OutputInterface::class);
|
||||
}
|
||||
|
||||
public function testServerEmptyString(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'server') {
|
||||
return '';
|
||||
} elseif ($arg === 'secret') {
|
||||
return 'my-test-secret';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->with('verify')
|
||||
->willReturn(true);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<error>Server cannot be empty.</error>'));
|
||||
$this->config->expects($this->never())
|
||||
->method('setAppValue');
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testSecretEmptyString(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'server') {
|
||||
return 'wss://signaling.test.com';
|
||||
} elseif ($arg === 'secret') {
|
||||
return '';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->with('verify')
|
||||
->willReturn(true);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<error>Secret cannot be empty.</error>'));
|
||||
$this->config->expects($this->never())
|
||||
->method('setAppValue');
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testAddServerToEmptyList(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'server') {
|
||||
return 'wss://signaling.test.com';
|
||||
} elseif ($arg === 'secret') {
|
||||
return 'my-test-secret';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->with('verify')
|
||||
->willReturn(true);
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'signaling_servers')
|
||||
->willReturn(json_encode([]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('signaling_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
'servers' => [
|
||||
[
|
||||
'server' => 'wss://signaling.test.com',
|
||||
'verify' => true
|
||||
]
|
||||
],
|
||||
'secret' => 'my-test-secret'
|
||||
]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>Added signaling server wss://signaling.test.com.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testAddServerToNonEmptyList(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'server') {
|
||||
return 'wss://signaling2.test.com';
|
||||
} elseif ($arg === 'secret') {
|
||||
return 'my-test-secret';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->with('verify')
|
||||
->willReturn(true);
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'signaling_servers')
|
||||
->willReturn(json_encode([
|
||||
'servers' => [
|
||||
[
|
||||
'server' => 'wss://signaling1.test.com',
|
||||
'verify' => true
|
||||
]
|
||||
],
|
||||
'secret' => 'my-test-secret'
|
||||
]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('signaling_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
'servers' => [
|
||||
[
|
||||
'server' => 'wss://signaling1.test.com',
|
||||
'verify' => true
|
||||
],
|
||||
[
|
||||
'server' => 'wss://signaling2.test.com',
|
||||
'verify' => true
|
||||
]
|
||||
],
|
||||
'secret' => 'my-test-secret'
|
||||
]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>Added signaling server wss://signaling2.test.com.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Talk\Tests\php\Command\Signaling;
|
||||
|
||||
use OCA\Talk\Command\Signaling\Delete;
|
||||
use OCP\IConfig;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class DeleteTest extends TestCase {
|
||||
protected IConfig&MockObject $config;
|
||||
protected InputInterface&MockObject $input;
|
||||
protected OutputInterface&MockObject $output;
|
||||
protected Delete $command;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
|
||||
$this->command = new Delete($this->config);
|
||||
|
||||
$this->input = $this->createMock(InputInterface::class);
|
||||
$this->output = $this->createMock(OutputInterface::class);
|
||||
}
|
||||
|
||||
public function testDeleteIfEmpty(): void {
|
||||
$this->input->method('getArgument')
|
||||
->with('server')
|
||||
->willReturn('wss://signaling.example.com');
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'signaling_servers')
|
||||
->willReturn('');
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('signaling_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
'servers' => [],
|
||||
'secret' => ''
|
||||
]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>There is nothing to delete.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testDelete(): void {
|
||||
$this->input->method('getArgument')
|
||||
->with('server')
|
||||
->willReturn('wss://signaling2.test.com');
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'signaling_servers')
|
||||
->willReturn(json_encode([
|
||||
'servers' => [
|
||||
[
|
||||
'server' => 'wss://signaling1.test.com',
|
||||
'verify' => false,
|
||||
],
|
||||
[
|
||||
'server' => 'wss://signaling2.test.com',
|
||||
'verify' => false,
|
||||
],
|
||||
[
|
||||
'server' => 'wss://signaling3.test.com',
|
||||
'verify' => false,
|
||||
]
|
||||
],
|
||||
'secret' => 'my-test-secret',
|
||||
]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('signaling_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
'servers' => [
|
||||
[
|
||||
'server' => 'wss://signaling1.test.com',
|
||||
'verify' => false,
|
||||
],
|
||||
[
|
||||
'server' => 'wss://signaling3.test.com',
|
||||
'verify' => false,
|
||||
]
|
||||
],
|
||||
'secret' => 'my-test-secret',
|
||||
]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>Deleted wss://signaling2.test.com.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testNothingToDelete(): void {
|
||||
$this->input->method('getArgument')
|
||||
->with('server')
|
||||
->willReturn('wss://signaling4.test.com');
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'signaling_servers')
|
||||
->willReturn(json_encode([
|
||||
'servers' => [
|
||||
[
|
||||
'server' => 'wss://signaling1.test.com',
|
||||
'verify' => false,
|
||||
]
|
||||
],
|
||||
'secret' => 'my-test-secret',
|
||||
]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('signaling_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
'servers' => [
|
||||
[
|
||||
'server' => 'wss://signaling1.test.com',
|
||||
'verify' => false,
|
||||
]
|
||||
],
|
||||
'secret' => 'my-test-secret',
|
||||
]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>There is nothing to delete.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Talk\Tests\php\Command\Signaling;
|
||||
|
||||
use OCA\Talk\Command\Signaling\ListCommand;
|
||||
use OCP\IConfig;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class ListCommandTest extends TestCase {
|
||||
protected IConfig&MockObject $config;
|
||||
protected InputInterface&MockObject $input;
|
||||
protected OutputInterface&MockObject $output;
|
||||
protected ListCommand&MockObject $command;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
|
||||
$this->command = $this->getMockBuilder(ListCommand::class)
|
||||
->setConstructorArgs([$this->config])
|
||||
->onlyMethods(['writeMixedInOutputFormat'])
|
||||
->getMock();
|
||||
|
||||
$this->input = $this->createMock(InputInterface::class);
|
||||
$this->output = $this->createMock(OutputInterface::class);
|
||||
}
|
||||
|
||||
public function testEmptyAppConfig(): void {
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'signaling_servers')
|
||||
->willReturn(json_encode([]));
|
||||
|
||||
$this->command->expects($this->once())
|
||||
->method('writeMixedInOutputFormat')
|
||||
->with(
|
||||
$this->equalTo($this->input),
|
||||
$this->equalTo($this->output),
|
||||
$this->equalTo([])
|
||||
);
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testAppConfigDataChanges(): void {
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'signaling_servers')
|
||||
->willReturn(json_encode([
|
||||
'servers' => [
|
||||
[
|
||||
'server' => 'wss://signaling.example.com',
|
||||
'verify' => true
|
||||
]
|
||||
],
|
||||
'secret' => 'my-test-secret'
|
||||
]));
|
||||
|
||||
$this->command->expects($this->once())
|
||||
->method('writeMixedInOutputFormat')
|
||||
->with(
|
||||
$this->equalTo($this->input),
|
||||
$this->equalTo($this->output),
|
||||
$this->equalTo([
|
||||
'servers' => [
|
||||
[
|
||||
'server' => 'wss://signaling.example.com',
|
||||
'verify' => true
|
||||
]
|
||||
],
|
||||
'secret' => 'my-test-secret'
|
||||
])
|
||||
);
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Talk\Tests\php\Command\Stun;
|
||||
|
||||
use OCA\Talk\Command\Stun\Add;
|
||||
use OCP\IConfig;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class AddTest extends TestCase {
|
||||
protected IConfig&MockObject $config;
|
||||
protected InputInterface&MockObject $input;
|
||||
protected OutputInterface&MockObject $output;
|
||||
protected Add $command;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
|
||||
$this->command = new Add($this->config);
|
||||
|
||||
$this->input = $this->createMock(InputInterface::class);
|
||||
$this->output = $this->createMock(OutputInterface::class);
|
||||
}
|
||||
|
||||
public function testMalformedServerString(): void {
|
||||
$this->input->method('getArgument')
|
||||
->with('server')
|
||||
->willReturn('stun.test.com');
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<error>Incorrect value. Must be stunserver:port.</error>'));
|
||||
$this->config->expects($this->never())
|
||||
->method('setAppValue');
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testAddServerToEmptyList(): void {
|
||||
$this->input->method('getArgument')
|
||||
->with('server')
|
||||
->willReturn('stun.test.com:443');
|
||||
$this->config->method('getAppValue')
|
||||
->with('spreed', 'stun_servers')
|
||||
->willReturn(json_encode([]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('stun_servers'),
|
||||
$this->equalTo(json_encode(['stun.test.com:443']))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>Added stun.test.com:443.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testAddServerToNonEmptyList(): void {
|
||||
$this->input->method('getArgument')
|
||||
->with('server')
|
||||
->willReturn('stun2.test.com:443');
|
||||
$this->config->method('getAppValue')
|
||||
->with('spreed', 'stun_servers')
|
||||
->willReturn(json_encode(['stun1.test.com:443']));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('stun_servers'),
|
||||
$this->equalTo(json_encode(['stun1.test.com:443', 'stun2.test.com:443']))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>Added stun2.test.com:443.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testAddDuplicateServer(): void {
|
||||
$this->input->method('getArgument')
|
||||
->with('server')
|
||||
->willReturn('stun.test.com:443');
|
||||
$this->config->method('getAppValue')
|
||||
->with('spreed', 'stun_servers')
|
||||
->willReturn(json_encode(['stun.test.com:443']));
|
||||
$this->config->expects($this->never())
|
||||
->method('setAppValue');
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<error>Server already exists.</error>'));
|
||||
|
||||
$this->assertSame(1, self::invokePrivate($this->command, 'execute', [$this->input, $this->output]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Talk\Tests\php\Command\Stun;
|
||||
|
||||
use OCA\Talk\Command\Stun\Delete;
|
||||
use OCP\IConfig;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class DeleteTest extends TestCase {
|
||||
protected IConfig&MockObject $config;
|
||||
protected InputInterface&MockObject $input;
|
||||
protected OutputInterface&MockObject $output;
|
||||
protected Delete $command;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
|
||||
$this->command = new Delete($this->config);
|
||||
|
||||
$this->input = $this->createMock(InputInterface::class);
|
||||
$this->output = $this->createMock(OutputInterface::class);
|
||||
}
|
||||
|
||||
public function testAddDefaultServerIfEmpty(): void {
|
||||
$this->input->method('getArgument')
|
||||
->with('server')
|
||||
->willReturn('stun1.test.com:443');
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'stun_servers')
|
||||
->willReturn('');
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('stun_servers'),
|
||||
$this->equalTo(json_encode(['stun.nextcloud.com:443']))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>You deleted all STUN servers. A default STUN server was added.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testDelete(): void {
|
||||
$this->input->method('getArgument')
|
||||
->with('server')
|
||||
->willReturn('stun1.test.com:443');
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'stun_servers')
|
||||
->willReturn(json_encode(['stun1.test.com:443', 'stun2.test.com:443']));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('stun_servers'),
|
||||
$this->equalTo(json_encode(['stun2.test.com:443']))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>Deleted stun1.test.com:443.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testNothingToDelete(): void {
|
||||
$this->input->method('getArgument')
|
||||
->with('server')
|
||||
->willReturn('stun3.test.com:443');
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'stun_servers')
|
||||
->willReturn(json_encode(['stun1.test.com:443']));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('stun_servers'),
|
||||
$this->equalTo(json_encode(['stun1.test.com:443']))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>There is nothing to delete.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Talk\Tests\php\Command\Stun;
|
||||
|
||||
use OCA\Talk\Command\Stun\ListCommand;
|
||||
use OCP\IConfig;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class ListCommandTest extends TestCase {
|
||||
protected IConfig&MockObject $config;
|
||||
protected InputInterface&MockObject $input;
|
||||
protected OutputInterface&MockObject $output;
|
||||
protected ListCommand&MockObject $command;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
|
||||
$this->command = $this->getMockBuilder(ListCommand::class)
|
||||
->setConstructorArgs([$this->config])
|
||||
->onlyMethods(['writeArrayInOutputFormat'])
|
||||
->getMock();
|
||||
|
||||
$this->input = $this->createMock(InputInterface::class);
|
||||
$this->output = $this->createMock(OutputInterface::class);
|
||||
}
|
||||
|
||||
public function testEmptyAppConfig(): void {
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'stun_servers')
|
||||
->willReturn(json_encode([]));
|
||||
|
||||
$this->command->expects($this->once())
|
||||
->method('writeArrayInOutputFormat')
|
||||
->with(
|
||||
$this->equalTo($this->input),
|
||||
$this->equalTo($this->output),
|
||||
$this->equalTo([])
|
||||
);
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testAppConfigDataChanges(): void {
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'stun_servers')
|
||||
->willReturn(json_encode([
|
||||
'stun.test.com:443',
|
||||
'stun2.test.com:443'
|
||||
]));
|
||||
|
||||
$this->command->expects($this->once())
|
||||
->method('writeArrayInOutputFormat')
|
||||
->with(
|
||||
$this->equalTo($this->input),
|
||||
$this->equalTo($this->output),
|
||||
$this->equalTo([
|
||||
'stun.test.com:443',
|
||||
'stun2.test.com:443'
|
||||
])
|
||||
);
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Talk\Tests\php\Command\Turn;
|
||||
|
||||
use OCA\Talk\Command\Turn\Add;
|
||||
use OCP\IConfig;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class AddTest extends TestCase {
|
||||
protected IConfig&MockObject $config;
|
||||
protected ISecureRandom&MockObject $secureRandom;
|
||||
protected InputInterface&MockObject $input;
|
||||
protected OutputInterface&MockObject $output;
|
||||
protected Add $command;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->secureRandom = $this->createMock(ISecureRandom::class);
|
||||
|
||||
$this->command = new Add($this->config, $this->secureRandom);
|
||||
|
||||
$this->input = $this->createMock(InputInterface::class);
|
||||
$this->output = $this->createMock(OutputInterface::class);
|
||||
}
|
||||
|
||||
public function testServerEmptyString(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return '';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'secret') {
|
||||
return 'my-test-secret';
|
||||
} elseif ($arg === 'generate-secret') {
|
||||
return false;
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<error>Server cannot be empty.</error>'));
|
||||
$this->config->expects($this->never())
|
||||
->method('setAppValue');
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testSecretEmpty(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn.test.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'secret') {
|
||||
return '';
|
||||
} elseif ($arg === 'generate-secret') {
|
||||
return false;
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<error>Secret cannot be empty.</error>'));
|
||||
$this->config->expects($this->never())
|
||||
->method('setAppValue');
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testGenerateSecret(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn.test.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'secret') {
|
||||
return null;
|
||||
} elseif ($arg === 'generate-secret') {
|
||||
return true;
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
|
||||
$this->secureRandom->expects($this->once())
|
||||
->method('generate')
|
||||
->willReturn('O2vWVFk5QRdJ/9clK4XIHbkxYXvVe6ySggANw4TG/B/HCtFzpi7v4GVNB/6wUZvA13v2EN4WgDk+gjATk9zhhc7B6FzxLNWOlQFBADg2aYHb+Ozse2BABDk3VUHCR+W9');
|
||||
$this->config->method('getAppValue')
|
||||
->with('spreed', 'turn_servers')
|
||||
->willReturn(json_encode([]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('turn_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn.test.com',
|
||||
'secret' => 'O2vWVFk5QRdJ/9clK4XIHbkxYXvVe6ySggANw4TG/B/HCtFzpi7v4GVNB/6wUZvA13v2EN4WgDk+gjATk9zhhc7B6FzxLNWOlQFBADg2aYHb+Ozse2BABDk3VUHCR+W9',
|
||||
'protocols' => 'udp,tcp'
|
||||
]
|
||||
]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>Added turn.test.com.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testSecretAndGenerateSecretOptions(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn.test.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'secret') {
|
||||
return 'my-test-secret';
|
||||
} elseif ($arg === 'generate-secret') {
|
||||
return true;
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<error>You must provide --secret or --generate-secret.</error>'));
|
||||
$this->config->expects($this->never())
|
||||
->method('setAppValue');
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testInvalidSchemesString(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'invalid-scheme';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn.test.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'secret') {
|
||||
return 'my-test-secret';
|
||||
} elseif ($arg === 'generate-secret') {
|
||||
return false;
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<error>Not allowed schemes, must be turn or turns or turn,turns.</error>'));
|
||||
$this->config->expects($this->never())
|
||||
->method('setAppValue');
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testInvalidProtocolsString(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn.test.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'invalid-protocol';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'secret') {
|
||||
return 'my-test-secret';
|
||||
} elseif ($arg === 'generate-secret') {
|
||||
return false;
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<error>Not allowed protocols, must be udp or tcp or udp,tcp.</error>'));
|
||||
$this->config->expects($this->never())
|
||||
->method('setAppValue');
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testAddServerToEmptyList(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn.test.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'secret') {
|
||||
return 'my-test-secret';
|
||||
} elseif ($arg === 'generate-secret') {
|
||||
return false;
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->config->method('getAppValue')
|
||||
->with('spreed', 'turn_servers')
|
||||
->willReturn(json_encode([]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('turn_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn.test.com',
|
||||
'secret' => 'my-test-secret',
|
||||
'protocols' => 'udp,tcp'
|
||||
]
|
||||
]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>Added turn.test.com.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testAddServerToNonEmptyList(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn2.test.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'secret') {
|
||||
return 'my-test-secret-2';
|
||||
} elseif ($arg === 'generate-secret') {
|
||||
return false;
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->config->method('getAppValue')
|
||||
->with('spreed', 'turn_servers')
|
||||
->willReturn(json_encode([
|
||||
[
|
||||
'schemes' => 'turn',
|
||||
'server' => 'turn1.test.com',
|
||||
'secret' => 'my-test-secret-1',
|
||||
'protocols' => 'udp,tcp'
|
||||
]
|
||||
]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('turn_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
[
|
||||
'schemes' => 'turn',
|
||||
'server' => 'turn1.test.com',
|
||||
'secret' => 'my-test-secret-1',
|
||||
'protocols' => 'udp,tcp'
|
||||
],
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn2.test.com',
|
||||
'secret' => 'my-test-secret-2',
|
||||
'protocols' => 'udp,tcp'
|
||||
]
|
||||
]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>Added turn2.test.com.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testServerSanitization(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'https://turn.test.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'secret') {
|
||||
return 'my-test-secret';
|
||||
} elseif ($arg === 'generate-secret') {
|
||||
return false;
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->config->method('getAppValue')
|
||||
->with('spreed', 'turn_servers')
|
||||
->willReturn(json_encode([]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('turn_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn.test.com',
|
||||
'secret' => 'my-test-secret',
|
||||
'protocols' => 'udp,tcp'
|
||||
]
|
||||
]))
|
||||
);
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testAddDuplicateServer(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn.test.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->input->method('getOption')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'secret') {
|
||||
return 'my-test-secret';
|
||||
} elseif ($arg === 'generate-secret') {
|
||||
return false;
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->config->method('getAppValue')
|
||||
->with('spreed', 'turn_servers')
|
||||
->willReturn(json_encode([[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn.test.com',
|
||||
'secret' => 'my-test-secret',
|
||||
'protocols' => 'udp,tcp'
|
||||
]]));
|
||||
$this->config->expects($this->never())
|
||||
->method('setAppValue');
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<error>Server already exists with the same configuration.</error>'));
|
||||
|
||||
$this->assertSame(1, self::invokePrivate($this->command, 'execute', [$this->input, $this->output]));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Talk\Tests\php\Command\Turn;
|
||||
|
||||
use OCA\Talk\Command\Turn\Delete;
|
||||
use OCP\IConfig;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class DeleteTest extends TestCase {
|
||||
protected IConfig&MockObject $config;
|
||||
protected InputInterface&MockObject $input;
|
||||
protected OutputInterface&MockObject $output;
|
||||
protected Delete $command;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
|
||||
$this->command = new Delete($this->config);
|
||||
|
||||
$this->input = $this->createMock(InputInterface::class);
|
||||
$this->output = $this->createMock(OutputInterface::class);
|
||||
}
|
||||
|
||||
public function testDeleteIfEmpty(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn.example.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'turn_servers')
|
||||
->willReturn('');
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('turn_servers'),
|
||||
$this->equalTo(json_encode([]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>There is nothing to delete.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testDelete(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn2.example.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'turn_servers')
|
||||
->willReturn(json_encode([
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn1.example.com',
|
||||
'secret' => 'my-test-secret-1',
|
||||
'protocols' => 'udp,tcp'
|
||||
]
|
||||
]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('turn_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn1.example.com',
|
||||
'secret' => 'my-test-secret-1',
|
||||
'protocols' => 'udp,tcp'
|
||||
]
|
||||
]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>There is nothing to delete.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testNothingToDelete(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn4.example.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'turn_servers')
|
||||
->willReturn(json_encode([
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn1.example.com',
|
||||
'secret' => 'my-test-secret-1',
|
||||
'protocols' => 'udp,tcp'
|
||||
],
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn2.example.com',
|
||||
'secret' => 'my-test-secret-2',
|
||||
'protocols' => 'udp,tcp'
|
||||
],
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn3.example.com',
|
||||
'secret' => 'my-test-secret-3',
|
||||
'protocols' => 'udp,tcp'
|
||||
],
|
||||
]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('turn_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn1.example.com',
|
||||
'secret' => 'my-test-secret-1',
|
||||
'protocols' => 'udp,tcp'
|
||||
],
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn2.example.com',
|
||||
'secret' => 'my-test-secret-2',
|
||||
'protocols' => 'udp,tcp'
|
||||
],
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn3.example.com',
|
||||
'secret' => 'my-test-secret-3',
|
||||
'protocols' => 'udp,tcp'
|
||||
],
|
||||
]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>There is nothing to delete.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testDeleteMatchingSchemes(): void {
|
||||
$this->input->method('getArgument')
|
||||
->willReturnCallback(function ($arg) {
|
||||
if ($arg === 'schemes') {
|
||||
return 'turn,turns';
|
||||
} elseif ($arg === 'server') {
|
||||
return 'turn.example.com';
|
||||
} elseif ($arg === 'protocols') {
|
||||
return 'udp,tcp';
|
||||
}
|
||||
throw new \Exception();
|
||||
});
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'turn_servers')
|
||||
->willReturn(json_encode([
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn.example.com',
|
||||
'secret' => 'my-test-secret-1',
|
||||
'protocols' => 'udp,tcp'
|
||||
],
|
||||
[
|
||||
'schemes' => 'turn',
|
||||
'server' => 'turn.example.com',
|
||||
'secret' => 'my-test-secret-1',
|
||||
'protocols' => 'udp,tcp'
|
||||
]
|
||||
]));
|
||||
$this->config->expects($this->once())
|
||||
->method('setAppValue')
|
||||
->with(
|
||||
$this->equalTo('spreed'),
|
||||
$this->equalTo('turn_servers'),
|
||||
$this->equalTo(json_encode([
|
||||
[
|
||||
'schemes' => 'turn',
|
||||
'server' => 'turn.example.com',
|
||||
'secret' => 'my-test-secret-1',
|
||||
'protocols' => 'udp,tcp'
|
||||
]
|
||||
]))
|
||||
);
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with($this->equalTo('<info>Deleted turn.example.com.</info>'));
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Talk\Tests\php\Command\Turn;
|
||||
|
||||
use OCA\Talk\Command\Turn\ListCommand;
|
||||
use OCP\IConfig;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class ListCommandTest extends TestCase {
|
||||
protected IConfig&MockObject $config;
|
||||
protected InputInterface&MockObject $input;
|
||||
protected OutputInterface&MockObject $output;
|
||||
protected ListCommand&MockObject $command;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
|
||||
$this->command = $this->getMockBuilder(ListCommand::class)
|
||||
->setConstructorArgs([$this->config])
|
||||
->onlyMethods(['writeMixedInOutputFormat'])
|
||||
->getMock();
|
||||
|
||||
$this->input = $this->createMock(InputInterface::class);
|
||||
$this->output = $this->createMock(OutputInterface::class);
|
||||
}
|
||||
|
||||
public function testEmptyAppConfig(): void {
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'turn_servers')
|
||||
->willReturn(json_encode([]));
|
||||
|
||||
$this->command->expects($this->once())
|
||||
->method('writeMixedInOutputFormat')
|
||||
->with(
|
||||
$this->equalTo($this->input),
|
||||
$this->equalTo($this->output),
|
||||
$this->equalTo([])
|
||||
);
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
|
||||
public function testAppConfigDataChanges(): void {
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'turn_servers')
|
||||
->willReturn(json_encode([
|
||||
[
|
||||
'server' => 'turn1.test.com',
|
||||
'secret' => 'my-sercret-1',
|
||||
'protocols' => 'tcp',
|
||||
],
|
||||
[
|
||||
'server' => 'turn2.test.com',
|
||||
'secret' => 'my-sercret-2',
|
||||
'protocols' => 'udp,tcp',
|
||||
],
|
||||
]));
|
||||
|
||||
$this->command->expects($this->once())
|
||||
->method('writeMixedInOutputFormat')
|
||||
->with(
|
||||
$this->equalTo($this->input),
|
||||
$this->equalTo($this->output),
|
||||
$this->equalTo([
|
||||
[
|
||||
'server' => 'turn1.test.com',
|
||||
'secret' => 'my-sercret-1',
|
||||
'protocols' => 'tcp',
|
||||
],
|
||||
[
|
||||
'server' => 'turn2.test.com',
|
||||
'secret' => 'my-sercret-2',
|
||||
'protocols' => 'udp,tcp',
|
||||
],
|
||||
])
|
||||
);
|
||||
|
||||
self::invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Talk\Tests\php;
|
||||
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Events\BeforeTurnServersGetEvent;
|
||||
use OCA\Talk\Tests\php\Mocks\GetTurnServerListener;
|
||||
use OCA\Talk\Vendor\Firebase\JWT\JWT;
|
||||
use OCA\Talk\Vendor\Firebase\JWT\Key;
|
||||
use OCP\AppFramework\Services\IAppConfig;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Config\IUserConfig;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IConfig;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class ConfigTest extends TestCase {
|
||||
private function createConfig(IConfig $config) {
|
||||
/** @var MockObject|IAppConfig $appConfig */
|
||||
$appConfig = $this->createMock(IAppConfig::class);
|
||||
/** @var MockObject|IUserConfig $appConfig */
|
||||
$userConfig = $this->createMock(IUserConfig::class);
|
||||
/** @var MockObject|ITimeFactory $timeFactory */
|
||||
$timeFactory = $this->createMock(ITimeFactory::class);
|
||||
/** @var MockObject|ISecureRandom $secureRandom */
|
||||
$secureRandom = $this->createMock(ISecureRandom::class);
|
||||
/** @var MockObject|IGroupManager $groupManager */
|
||||
$groupManager = $this->createMock(IGroupManager::class);
|
||||
/** @var MockObject|IUserManager $userManager */
|
||||
$userManager = $this->createMock(IUserManager::class);
|
||||
/** @var MockObject|IURLGenerator $urlGenerator */
|
||||
$urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
/** @var MockObject|IEventDispatcher $dispatcher */
|
||||
$dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
|
||||
$helper = new Config($config, $appConfig, $userConfig, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher);
|
||||
return $helper;
|
||||
}
|
||||
|
||||
public function testGetStunServers(): void {
|
||||
$servers = [
|
||||
'stun1.example.com:443',
|
||||
'stun2.example.com:129',
|
||||
];
|
||||
|
||||
/** @var MockObject|IConfig $config */
|
||||
$config = $this->createMock(IConfig::class);
|
||||
$config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'stun_servers', json_encode(['stun.nextcloud.com:443']))
|
||||
->willReturn(json_encode($servers));
|
||||
$config
|
||||
->expects($this->once())
|
||||
->method('getSystemValueBool')
|
||||
->with('has_internet_connection', true)
|
||||
->willReturn(true);
|
||||
|
||||
$helper = $this->createConfig($config);
|
||||
$this->assertSame($helper->getStunServers(), $servers);
|
||||
}
|
||||
|
||||
public function testGetDefaultStunServer(): void {
|
||||
/** @var MockObject|IConfig $config */
|
||||
$config = $this->createMock(IConfig::class);
|
||||
$config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'stun_servers', json_encode(['stun.nextcloud.com:443']))
|
||||
->willReturn(json_encode([]));
|
||||
$config
|
||||
->expects($this->once())
|
||||
->method('getSystemValueBool')
|
||||
->with('has_internet_connection', true)
|
||||
->willReturn(true);
|
||||
|
||||
$helper = $this->createConfig($config);
|
||||
$this->assertSame(['stun.nextcloud.com:443'], $helper->getStunServers());
|
||||
}
|
||||
|
||||
public function testGetDefaultStunServerNoInternet(): void {
|
||||
/** @var MockObject|IConfig $config */
|
||||
$config = $this->createMock(IConfig::class);
|
||||
$config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'stun_servers', json_encode(['stun.nextcloud.com:443']))
|
||||
->willReturn(json_encode([]));
|
||||
$config
|
||||
->expects($this->once())
|
||||
->method('getSystemValueBool')
|
||||
->with('has_internet_connection', true)
|
||||
->willReturn(false);
|
||||
|
||||
$helper = $this->createConfig($config);
|
||||
$this->assertSame([], $helper->getStunServers());
|
||||
}
|
||||
|
||||
public function testGenerateTurnSettings(): void {
|
||||
/** @var MockObject|IConfig $config */
|
||||
$config = $this->createMock(IConfig::class);
|
||||
$config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'turn_servers', '')
|
||||
->willReturn(json_encode([
|
||||
[
|
||||
// No scheme explicitly given
|
||||
'server' => 'turn.example.org:3478',
|
||||
'secret' => 'thisisasupersecretsecret',
|
||||
'protocols' => 'udp,tcp',
|
||||
],
|
||||
[
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn2.example.com:5349',
|
||||
'secret' => 'ThisIsAlsoSuperSecret',
|
||||
'protocols' => 'udp',
|
||||
],
|
||||
[
|
||||
'schemes' => 'turns',
|
||||
'server' => 'turn-tls.example.com:443',
|
||||
'secret' => 'ThisIsAlsoSuperSecret',
|
||||
'protocols' => 'tcp',
|
||||
],
|
||||
]));
|
||||
|
||||
/** @var MockObject|ITimeFactory $timeFactory */
|
||||
$timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$timeFactory
|
||||
->expects($this->once())
|
||||
->method('getTime')
|
||||
->willReturn(1479743025);
|
||||
|
||||
/** @var MockObject|IAppConfig $appConfig */
|
||||
$appConfig = $this->createMock(IAppConfig::class);
|
||||
/** @var MockObject|IUserConfig $appConfig */
|
||||
$userConfig = $this->createMock(IUserConfig::class);
|
||||
/** @var MockObject|IGroupManager $groupManager */
|
||||
$groupManager = $this->createMock(IGroupManager::class);
|
||||
/** @var MockObject|IUserManager $userManager */
|
||||
$userManager = $this->createMock(IUserManager::class);
|
||||
/** @var MockObject|IURLGenerator $urlGenerator */
|
||||
$urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
/** @var MockObject|IEventDispatcher $dispatcher */
|
||||
$dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
|
||||
/** @var MockObject|ISecureRandom $secureRandom */
|
||||
$secureRandom = $this->createMock(ISecureRandom::class);
|
||||
$secureRandom
|
||||
->expects($this->once())
|
||||
->method('generate')
|
||||
->with(16)
|
||||
->willReturn('abcdefghijklmnop');
|
||||
$helper = new Config($config, $appConfig, $userConfig, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher);
|
||||
|
||||
//
|
||||
$settings = $helper->getTurnSettings();
|
||||
$this->assertEquals(3, count($settings));
|
||||
$this->assertSame([
|
||||
'schemes' => 'turn',
|
||||
'server' => 'turn.example.org:3478',
|
||||
'username' => '1479829425:abcdefghijklmnop',
|
||||
'password' => '4VJLVbihLzuxgMfDrm5C3zy8kLQ=',
|
||||
'protocols' => 'udp,tcp',
|
||||
], $settings[0]);
|
||||
$this->assertSame([
|
||||
'schemes' => 'turn,turns',
|
||||
'server' => 'turn2.example.com:5349',
|
||||
'username' => '1479829425:abcdefghijklmnop',
|
||||
'password' => 'Ol9DEqnvyN4g+IAM+vFnqhfWUTE=',
|
||||
'protocols' => 'udp',
|
||||
], $settings[1]);
|
||||
$this->assertSame([
|
||||
'schemes' => 'turns',
|
||||
'server' => 'turn-tls.example.com:443',
|
||||
'username' => '1479829425:abcdefghijklmnop',
|
||||
'password' => 'Ol9DEqnvyN4g+IAM+vFnqhfWUTE=',
|
||||
'protocols' => 'tcp',
|
||||
], $settings[2]);
|
||||
}
|
||||
|
||||
public function testGenerateTurnSettingsEmpty(): void {
|
||||
/** @var MockObject|IConfig $config */
|
||||
$config = $this->createMock(IConfig::class);
|
||||
$config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'turn_servers', '')
|
||||
->willReturn(json_encode([]));
|
||||
|
||||
$helper = $this->createConfig($config);
|
||||
|
||||
$settings = $helper->getTurnSettings();
|
||||
$this->assertEquals(0, count($settings));
|
||||
}
|
||||
|
||||
public function testGenerateTurnSettingsEvent(): void {
|
||||
/** @var MockObject|IConfig $config */
|
||||
$config = $this->createMock(IConfig::class);
|
||||
$config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('spreed', 'turn_servers', '')
|
||||
->willReturn(json_encode([]));
|
||||
|
||||
/** @var MockObject|IAppConfig $appConfig */
|
||||
$appConfig = $this->createMock(IAppConfig::class);
|
||||
/** @var MockObject|IUserConfig $appConfig */
|
||||
$userConfig = $this->createMock(IUserConfig::class);
|
||||
|
||||
/** @var MockObject|ITimeFactory $timeFactory */
|
||||
$timeFactory = $this->createMock(ITimeFactory::class);
|
||||
|
||||
/** @var MockObject|IGroupManager $groupManager */
|
||||
$groupManager = $this->createMock(IGroupManager::class);
|
||||
|
||||
/** @var MockObject|IUserManager $userManager */
|
||||
$userManager = $this->createMock(IUserManager::class);
|
||||
|
||||
/** @var MockObject|IURLGenerator $urlGenerator */
|
||||
$urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
|
||||
/** @var MockObject|ISecureRandom $secureRandom */
|
||||
$secureRandom = $this->createMock(ISecureRandom::class);
|
||||
|
||||
/** @var IEventDispatcher $dispatcher */
|
||||
$dispatcher = \OCP\Server::get(IEventDispatcher::class);
|
||||
|
||||
$servers = [
|
||||
[
|
||||
'schemes' => 'turn',
|
||||
'server' => 'turn.domain.invalid',
|
||||
'username' => 'john',
|
||||
'password' => 'abcde',
|
||||
'protocols' => 'udp,tcp',
|
||||
],
|
||||
[
|
||||
'schemes' => 'turns',
|
||||
'server' => 'turns.domain.invalid',
|
||||
'username' => 'jane',
|
||||
'password' => 'ABCDE',
|
||||
'protocols' => 'tcp',
|
||||
],
|
||||
];
|
||||
|
||||
$dispatcher->addServiceListener(BeforeTurnServersGetEvent::class, GetTurnServerListener::class);
|
||||
|
||||
$helper = new Config($config, $appConfig, $userConfig, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher);
|
||||
|
||||
$settings = $helper->getTurnSettings();
|
||||
$this->assertSame($servers, $settings);
|
||||
}
|
||||
|
||||
public static function dataGetWebSocketDomainForSignalingServer(): array {
|
||||
return [
|
||||
['http://blabla.nextcloud.com', 'ws://blabla.nextcloud.com'],
|
||||
['http://blabla.nextcloud.com/', 'ws://blabla.nextcloud.com'],
|
||||
['http://blabla.nextcloud.com/signaling', 'ws://blabla.nextcloud.com'],
|
||||
['http://blabla.nextcloud.com/signaling/', 'ws://blabla.nextcloud.com'],
|
||||
['http://blabla.nextcloud.com:80', 'ws://blabla.nextcloud.com:80'],
|
||||
['http://blabla.nextcloud.com:80/', 'ws://blabla.nextcloud.com:80'],
|
||||
['http://blabla.nextcloud.com:80/signaling', 'ws://blabla.nextcloud.com:80'],
|
||||
['http://blabla.nextcloud.com:80/signaling/', 'ws://blabla.nextcloud.com:80'],
|
||||
['http://blabla.nextcloud.com:8000', 'ws://blabla.nextcloud.com:8000'],
|
||||
['http://blabla.nextcloud.com:8000/', 'ws://blabla.nextcloud.com:8000'],
|
||||
['http://blabla.nextcloud.com:8000/signaling', 'ws://blabla.nextcloud.com:8000'],
|
||||
['http://blabla.nextcloud.com:8000/signaling/', 'ws://blabla.nextcloud.com:8000'],
|
||||
|
||||
['https://blabla.nextcloud.com', 'wss://blabla.nextcloud.com'],
|
||||
['https://blabla.nextcloud.com/', 'wss://blabla.nextcloud.com'],
|
||||
['https://blabla.nextcloud.com/signaling', 'wss://blabla.nextcloud.com'],
|
||||
['https://blabla.nextcloud.com/signaling/', 'wss://blabla.nextcloud.com'],
|
||||
['https://blabla.nextcloud.com:443', 'wss://blabla.nextcloud.com:443'],
|
||||
['https://blabla.nextcloud.com:443/', 'wss://blabla.nextcloud.com:443'],
|
||||
['https://blabla.nextcloud.com:443/signaling', 'wss://blabla.nextcloud.com:443'],
|
||||
['https://blabla.nextcloud.com:443/signaling/', 'wss://blabla.nextcloud.com:443'],
|
||||
['https://blabla.nextcloud.com:8443', 'wss://blabla.nextcloud.com:8443'],
|
||||
['https://blabla.nextcloud.com:8443/', 'wss://blabla.nextcloud.com:8443'],
|
||||
['https://blabla.nextcloud.com:8443/signaling', 'wss://blabla.nextcloud.com:8443'],
|
||||
['https://blabla.nextcloud.com:8443/signaling/', 'wss://blabla.nextcloud.com:8443'],
|
||||
|
||||
['ws://blabla.nextcloud.com', 'ws://blabla.nextcloud.com'],
|
||||
['ws://blabla.nextcloud.com/', 'ws://blabla.nextcloud.com'],
|
||||
['ws://blabla.nextcloud.com/signaling', 'ws://blabla.nextcloud.com'],
|
||||
['ws://blabla.nextcloud.com/signaling/', 'ws://blabla.nextcloud.com'],
|
||||
['ws://blabla.nextcloud.com:80', 'ws://blabla.nextcloud.com:80'],
|
||||
['ws://blabla.nextcloud.com:80/', 'ws://blabla.nextcloud.com:80'],
|
||||
['ws://blabla.nextcloud.com:80/signaling', 'ws://blabla.nextcloud.com:80'],
|
||||
['ws://blabla.nextcloud.com:80/signaling/', 'ws://blabla.nextcloud.com:80'],
|
||||
['ws://blabla.nextcloud.com:8000', 'ws://blabla.nextcloud.com:8000'],
|
||||
['ws://blabla.nextcloud.com:8000/', 'ws://blabla.nextcloud.com:8000'],
|
||||
['ws://blabla.nextcloud.com:8000/signaling', 'ws://blabla.nextcloud.com:8000'],
|
||||
['ws://blabla.nextcloud.com:8000/signaling/', 'ws://blabla.nextcloud.com:8000'],
|
||||
|
||||
['wss://blabla.nextcloud.com', 'wss://blabla.nextcloud.com'],
|
||||
['wss://blabla.nextcloud.com/', 'wss://blabla.nextcloud.com'],
|
||||
['wss://blabla.nextcloud.com/signaling', 'wss://blabla.nextcloud.com'],
|
||||
['wss://blabla.nextcloud.com/signaling/', 'wss://blabla.nextcloud.com'],
|
||||
['wss://blabla.nextcloud.com:443', 'wss://blabla.nextcloud.com:443'],
|
||||
['wss://blabla.nextcloud.com:443/', 'wss://blabla.nextcloud.com:443'],
|
||||
['wss://blabla.nextcloud.com:443/signaling', 'wss://blabla.nextcloud.com:443'],
|
||||
['wss://blabla.nextcloud.com:443/signaling/', 'wss://blabla.nextcloud.com:443'],
|
||||
['wss://blabla.nextcloud.com:8443', 'wss://blabla.nextcloud.com:8443'],
|
||||
['wss://blabla.nextcloud.com:8443/', 'wss://blabla.nextcloud.com:8443'],
|
||||
['wss://blabla.nextcloud.com:8443/signaling', 'wss://blabla.nextcloud.com:8443'],
|
||||
['wss://blabla.nextcloud.com:8443/signaling/', 'wss://blabla.nextcloud.com:8443'],
|
||||
|
||||
// Admin got interrupted before finishing typing
|
||||
['wss://', ''],
|
||||
['ws://', ''],
|
||||
['https://', ''],
|
||||
['http://', ''],
|
||||
['wss:/', ''],
|
||||
['https:/', ''],
|
||||
['wss:', ''],
|
||||
['https:', ''],
|
||||
['wss', 'wss'],
|
||||
['https', 'https'],
|
||||
['ws', 'ws'],
|
||||
['http', 'http'],
|
||||
['w', 'w'],
|
||||
['htt', 'htt'],
|
||||
['ht', 'ht'],
|
||||
['h', 'h'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string $expectedWebSocketDomain
|
||||
*/
|
||||
#[DataProvider('dataGetWebSocketDomainForSignalingServer')]
|
||||
public function testGetWebSocketDomainForSignalingServer($url, $expectedWebSocketDomain): void {
|
||||
/** @var MockObject|IConfig $config */
|
||||
$config = $this->createMock(IConfig::class);
|
||||
|
||||
$helper = $this->createConfig($config);
|
||||
|
||||
$this->assertEquals(
|
||||
$expectedWebSocketDomain,
|
||||
self::invokePrivate($helper, 'getWebSocketDomainForSignalingServer', [$url])
|
||||
);
|
||||
}
|
||||
|
||||
public static function dataTicketV2Algorithm(): array {
|
||||
return [
|
||||
['ES384'],
|
||||
['ES256'],
|
||||
['RS256'],
|
||||
['RS384'],
|
||||
['RS512'],
|
||||
['EdDSA'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataTicketV2Algorithm')]
|
||||
public function testSignalingTicketV2User(string $algo): void {
|
||||
$config = \OCP\Server::get(IConfig::class);
|
||||
/** @var MockObject|IAppConfig $appConfig */
|
||||
$appConfig = $this->createMock(IAppConfig::class);
|
||||
/** @var MockObject|IUserConfig $appConfig */
|
||||
$userConfig = $this->createMock(IUserConfig::class);
|
||||
/** @var MockObject|ITimeFactory $timeFactory */
|
||||
$timeFactory = $this->createMock(ITimeFactory::class);
|
||||
/** @var MockObject|ISecureRandom $secureRandom */
|
||||
$secureRandom = $this->createMock(ISecureRandom::class);
|
||||
/** @var MockObject|IGroupManager $groupManager */
|
||||
$groupManager = $this->createMock(IGroupManager::class);
|
||||
/** @var MockObject|IUserManager $userManager */
|
||||
$userManager = $this->createMock(IUserManager::class);
|
||||
/** @var MockObject|IURLGenerator $urlGenerator */
|
||||
$urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
/** @var MockObject|IEventDispatcher $dispatcher */
|
||||
$dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
/** @var MockObject|IUser $user */
|
||||
$user = $this->createMock(IUser::class);
|
||||
|
||||
$now = time();
|
||||
$timeFactory
|
||||
->expects($this->once())
|
||||
->method('getTime')
|
||||
->willReturn($now);
|
||||
$urlGenerator
|
||||
->expects($this->once())
|
||||
->method('getAbsoluteURL')
|
||||
->with('')
|
||||
->willReturn('https://domain.invalid/nextcloud');
|
||||
$userManager
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with('user1')
|
||||
->willReturn($user);
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getUID')
|
||||
->willReturn('user1');
|
||||
$user
|
||||
->expects($this->once())
|
||||
->method('getDisplayName')
|
||||
->willReturn('Jane Doe');
|
||||
|
||||
$helper = new Config($config, $appConfig, $userConfig, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher);
|
||||
|
||||
$config->setAppValue('spreed', 'signaling_token_alg', $algo);
|
||||
// Make sure new keys are generated.
|
||||
$config->deleteAppValue('spreed', 'signaling_token_privkey_' . strtolower($algo));
|
||||
$config->deleteAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo));
|
||||
$ticket = $helper->getSignalingTicket(Config::SIGNALING_TICKET_V2, 'user1');
|
||||
$this->assertNotNull($ticket);
|
||||
|
||||
$key = new Key($config->getAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo)), $algo);
|
||||
$decoded = JWT::decode($ticket, $key);
|
||||
|
||||
$this->assertEquals($now, $decoded->iat);
|
||||
$this->assertEquals('https://domain.invalid/nextcloud', $decoded->iss);
|
||||
$this->assertEquals('user1', $decoded->sub);
|
||||
$this->assertSame(['displayname' => 'Jane Doe'], (array)$decoded->userdata);
|
||||
}
|
||||
|
||||
#[DataProvider('dataTicketV2Algorithm')]
|
||||
public function testSignalingTicketV2Anonymous(string $algo): void {
|
||||
/** @var IConfig $config */
|
||||
$config = \OCP\Server::get(IConfig::class);
|
||||
/** @var MockObject|IAppConfig $appConfig */
|
||||
$appConfig = $this->createMock(IAppConfig::class);
|
||||
/** @var MockObject|IUserConfig $appConfig */
|
||||
$userConfig = $this->createMock(IUserConfig::class);
|
||||
/** @var MockObject|ITimeFactory $timeFactory */
|
||||
$timeFactory = $this->createMock(ITimeFactory::class);
|
||||
/** @var MockObject|ISecureRandom $secureRandom */
|
||||
$secureRandom = $this->createMock(ISecureRandom::class);
|
||||
/** @var MockObject|IGroupManager $groupManager */
|
||||
$groupManager = $this->createMock(IGroupManager::class);
|
||||
/** @var MockObject|IUserManager $userManager */
|
||||
$userManager = $this->createMock(IUserManager::class);
|
||||
/** @var MockObject|IURLGenerator $urlGenerator */
|
||||
$urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
/** @var MockObject|IEventDispatcher $dispatcher */
|
||||
$dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
|
||||
$now = time();
|
||||
$timeFactory
|
||||
->expects($this->once())
|
||||
->method('getTime')
|
||||
->willReturn($now);
|
||||
$urlGenerator
|
||||
->expects($this->once())
|
||||
->method('getAbsoluteURL')
|
||||
->with('')
|
||||
->willReturn('https://domain.invalid/nextcloud');
|
||||
|
||||
$helper = new Config($config, $appConfig, $userConfig, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher);
|
||||
|
||||
$config->setAppValue('spreed', 'signaling_token_alg', $algo);
|
||||
// Make sure new keys are generated.
|
||||
$config->deleteAppValue('spreed', 'signaling_token_privkey_' . strtolower($algo));
|
||||
$config->deleteAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo));
|
||||
$ticket = $helper->getSignalingTicket(Config::SIGNALING_TICKET_V2, null);
|
||||
$this->assertNotNull($ticket);
|
||||
|
||||
$key = new Key($config->getAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo)), $algo);
|
||||
$decoded = JWT::decode($ticket, $key);
|
||||
|
||||
$this->assertEquals($now, $decoded->iat);
|
||||
$this->assertEquals('https://domain.invalid/nextcloud', $decoded->iss);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\Unit;
|
||||
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Test\TestCase;
|
||||
|
||||
class EventDocumentationTest extends TestCase {
|
||||
public static function dataEventDocumentation(): array {
|
||||
$dir = new \DirectoryIterator(__DIR__ . '/../../lib/Events');
|
||||
|
||||
$data = [];
|
||||
foreach ($dir as $fileinfo) {
|
||||
if (!$fileinfo->isDot()) {
|
||||
$data[] = ['OCA\\Talk\\Events\\' . substr($fileinfo->getFilename(), 0, -4)];
|
||||
}
|
||||
}
|
||||
sort($data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
#[DataProvider('dataEventDocumentation')]
|
||||
public function testEventDocumentation(string $eventClass): void {
|
||||
$reflectionClass = new \ReflectionClass($eventClass);
|
||||
if ($reflectionClass->isAbstract()) {
|
||||
self::assertTrue(true, 'Abstract event class ' . $eventClass . ' does not have to be documented');
|
||||
return;
|
||||
}
|
||||
|
||||
$classDocBlock = $reflectionClass->getDocComment();
|
||||
if (is_string($classDocBlock) && str_contains($classDocBlock, '@deprecated')) {
|
||||
self::assertTrue(true, 'Deprecated event ' . $eventClass . ' does not have to be documented');
|
||||
return;
|
||||
}
|
||||
if (is_string($classDocBlock) && str_contains($classDocBlock, '@internal')) {
|
||||
self::assertTrue(true, 'Internal event ' . $eventClass . ' does not have to be documented');
|
||||
return;
|
||||
}
|
||||
|
||||
$docs = file_get_contents(__DIR__ . '/../../docs/events.md');
|
||||
$eventIsDocumented = str_contains($docs, 'Before event: `' . $eventClass . '`')
|
||||
|| str_contains($docs, 'After event: `' . $eventClass . '`')
|
||||
|| str_contains($docs, 'Final event: `' . $eventClass . '`')
|
||||
|| str_contains($docs, 'Event: `' . $eventClass . '`');
|
||||
self::assertTrue($eventIsDocumented, 'Asserting that event ' . $eventClass . ' is documented');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,463 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Federation;
|
||||
|
||||
use OC\Federation\CloudFederationShare;
|
||||
use OCA\FederatedFileSharing\AddressHandler;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Federation\BackendNotifier;
|
||||
use OCA\Talk\Federation\CloudFederationProviderTalk;
|
||||
use OCA\Talk\Federation\FederationManager;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\UserConverter;
|
||||
use OCA\Talk\Federation\RestrictionValidator;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Model\AttendeeMapper;
|
||||
use OCA\Talk\Model\Invitation;
|
||||
use OCA\Talk\Model\InvitationMapper;
|
||||
use OCA\Talk\Model\ProxyCacheMessageMapper;
|
||||
use OCA\Talk\Model\RetryNotificationMapper;
|
||||
use OCA\Talk\Notification\FederationChatNotifier;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\ProxyCacheMessageService;
|
||||
use OCA\Talk\Service\RoomService;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Services\IAppConfig;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Federation\ICloudFederationFactory;
|
||||
use OCP\Federation\ICloudFederationNotification;
|
||||
use OCP\Federation\ICloudFederationProviderManager;
|
||||
use OCP\Federation\ICloudFederationShare;
|
||||
use OCP\Federation\ICloudId;
|
||||
use OCP\Federation\ICloudIdManager;
|
||||
use OCP\Http\Client\IResponse;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\ISession;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Notification\IManager as INotificationManager;
|
||||
use OCP\Notification\INotification;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class FederationTest extends TestCase {
|
||||
protected FederationManager&MockObject $federationManager;
|
||||
protected ICloudIdManager&MockObject $cloudIdManager;
|
||||
protected ICloudFederationProviderManager&MockObject $cloudFederationProviderManager;
|
||||
protected ICloudFederationFactory&MockObject $cloudFederationFactory;
|
||||
protected Config&MockObject $config;
|
||||
protected IAppConfig&MockObject $appConfig;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
protected AddressHandler&MockObject $addressHandler;
|
||||
protected IUserManager&MockObject $userManager;
|
||||
protected IAppManager&MockObject $appManager;
|
||||
protected IURLGenerator&MockObject $url;
|
||||
protected INotificationManager&MockObject $notificationManager;
|
||||
protected AttendeeMapper&MockObject $attendeeMapper;
|
||||
protected ProxyCacheMessageMapper&MockObject $proxyCacheMessageMapper;
|
||||
protected ProxyCacheMessageService&MockObject $proxyCacheMessageService;
|
||||
protected FederationChatNotifier&MockObject $federationChatNotifier;
|
||||
protected UserConverter&MockObject $userConverter;
|
||||
protected ICacheFactory&MockObject $cacheFactory;
|
||||
protected RetryNotificationMapper&MockObject $retryNotificationMapper;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected RestrictionValidator&MockObject $restrictionValidator;
|
||||
protected ?CloudFederationProviderTalk $cloudFederationProvider = null;
|
||||
protected ?BackendNotifier $backendNotifier = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->cloudIdManager = $this->createMock(ICloudIdManager::class);
|
||||
$this->cloudFederationProviderManager = $this->createMock(ICloudFederationProviderManager::class);
|
||||
$this->cloudFederationFactory = $this->createMock(ICloudFederationFactory::class);
|
||||
$this->addressHandler = $this->createMock(AddressHandler::class);
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->attendeeMapper = $this->createMock(AttendeeMapper::class);
|
||||
$this->config = $this->createMock(Config::class);
|
||||
$this->appConfig = $this->createMock(IAppConfig::class);
|
||||
$this->appManager = $this->createMock(IAppManager::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->url = $this->createMock(IURLGenerator::class);
|
||||
$this->proxyCacheMessageMapper = $this->createMock(ProxyCacheMessageMapper::class);
|
||||
$this->proxyCacheMessageService = $this->createMock(ProxyCacheMessageService::class);
|
||||
$this->cacheFactory = $this->createMock(ICacheFactory::class);
|
||||
$this->retryNotificationMapper = $this->createMock(RetryNotificationMapper::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->restrictionValidator = $this->createMock(RestrictionValidator::class);
|
||||
|
||||
$this->backendNotifier = new BackendNotifier(
|
||||
$this->cloudFederationFactory,
|
||||
$this->addressHandler,
|
||||
$this->logger,
|
||||
$this->cloudFederationProviderManager,
|
||||
$this->userManager,
|
||||
$this->url,
|
||||
$this->retryNotificationMapper,
|
||||
$this->timeFactory,
|
||||
$this->cloudIdManager,
|
||||
$this->restrictionValidator,
|
||||
);
|
||||
|
||||
$this->federationManager = $this->createMock(FederationManager::class);
|
||||
$this->notificationManager = $this->createMock(INotificationManager::class);
|
||||
$this->federationChatNotifier = $this->createMock(FederationChatNotifier::class);
|
||||
$this->userConverter = $this->createMock(UserConverter::class);
|
||||
|
||||
$this->cloudFederationProvider = new CloudFederationProviderTalk(
|
||||
$this->cloudIdManager,
|
||||
$this->userManager,
|
||||
$this->addressHandler,
|
||||
$this->federationManager,
|
||||
$this->config,
|
||||
$this->appConfig,
|
||||
$this->notificationManager,
|
||||
$this->createMock(ParticipantService::class),
|
||||
$this->createMock(RoomService::class),
|
||||
$this->attendeeMapper,
|
||||
$this->createMock(InvitationMapper::class),
|
||||
$this->createMock(Manager::class),
|
||||
$this->createMock(ISession::class),
|
||||
$this->createMock(IEventDispatcher::class),
|
||||
$this->logger,
|
||||
$this->proxyCacheMessageMapper,
|
||||
$this->proxyCacheMessageService,
|
||||
$this->federationChatNotifier,
|
||||
$this->userConverter,
|
||||
$this->timeFactory,
|
||||
$this->cacheFactory,
|
||||
);
|
||||
}
|
||||
|
||||
public function testSendRemoteShareWithOwner(): void {
|
||||
$cloudShare = $this->createMock(ICloudFederationShare::class);
|
||||
|
||||
$providerId = '3';
|
||||
$token = 'abcdefghijklmno';
|
||||
$shareWith = 'test@remote.test.local';
|
||||
$name = 'abcdefgh';
|
||||
$owner = 'Owner\'s name';
|
||||
$ownerId = 'owner';
|
||||
$ownerFederatedId = $ownerId . '@test.local';
|
||||
$sharedByDisplayName = 'Owner\'s name';
|
||||
$sharedByFederatedId = 'owner@test.local';
|
||||
$shareType = 'user';
|
||||
$roomType = Room::TYPE_GROUP;
|
||||
$roomName = 'Room name';
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$attendee = $this->createStub(Attendee::class);
|
||||
$ownerUser = $this->createMock(IUser::class);
|
||||
$sharedBy = $this->createMock(IUser::class);
|
||||
$sharedBy->expects($this->once())
|
||||
->method('getCloudId')
|
||||
->with()
|
||||
->willReturn($sharedByFederatedId);
|
||||
$sharedBy->expects($this->once())
|
||||
->method('getDisplayName')
|
||||
->with()
|
||||
->willReturn($sharedByDisplayName);
|
||||
|
||||
$room->expects($this->once())
|
||||
->method('getName')
|
||||
->with()
|
||||
->willReturn($roomName);
|
||||
|
||||
$room->expects($this->once())
|
||||
->method('getType')
|
||||
->with()
|
||||
->willReturn($roomType);
|
||||
|
||||
$room->expects($this->once())
|
||||
->method('getToken')
|
||||
->with()
|
||||
->willReturn($name);
|
||||
|
||||
$this->userManager->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn($ownerUser);
|
||||
|
||||
$ownerUser->expects($this->once())
|
||||
->method('getCloudId')
|
||||
->with()
|
||||
->willReturn($ownerFederatedId);
|
||||
|
||||
$ownerUser->expects($this->once())
|
||||
->method('getDisplayName')
|
||||
->with()
|
||||
->willReturn($owner);
|
||||
|
||||
$this->cloudFederationFactory->expects($this->once())
|
||||
->method('getCloudFederationShare')
|
||||
->with(
|
||||
$shareWith,
|
||||
$name,
|
||||
'',
|
||||
$providerId,
|
||||
$ownerFederatedId,
|
||||
$owner,
|
||||
$sharedByFederatedId,
|
||||
$sharedByDisplayName,
|
||||
$token,
|
||||
$shareType,
|
||||
'talk-room'
|
||||
)
|
||||
->willReturn($cloudShare);
|
||||
|
||||
$this->cloudFederationProviderManager->expects($this->once())
|
||||
->method('sendCloudShare')
|
||||
->with($cloudShare);
|
||||
|
||||
$cloudId = $this->createMock(ICloudId::class);
|
||||
$cloudId->method('getRemote')
|
||||
->willReturn('remote.test.local');
|
||||
$cloudId->method('getUser')
|
||||
->willReturn('test');
|
||||
|
||||
$this->cloudIdManager->expects($this->once())
|
||||
->method('resolveCloudId')
|
||||
->with($shareWith)
|
||||
->willReturn($cloudId);
|
||||
|
||||
$this->appConfig->method('getAppValueBool')
|
||||
->willReturnMap([
|
||||
['federation_outgoing_enabled', true, false, true],
|
||||
['federation_only_trusted_servers', false, false, false],
|
||||
]);
|
||||
|
||||
$this->config->method('isFederationEnabledForUserId')
|
||||
->with($sharedBy)
|
||||
->willReturn(true);
|
||||
|
||||
$this->backendNotifier->sendRemoteShare($providerId, $token, $shareWith, $sharedBy, $shareType, $room, $attendee);
|
||||
}
|
||||
|
||||
public function testReceiveRemoteShare(): void {
|
||||
$providerId = '3';
|
||||
$token = 'abcdefghijklmno';
|
||||
$shareWith = 'test@remote.test.local';
|
||||
$name = 'abcdefgh';
|
||||
$owner = 'Owner\'s name';
|
||||
$ownerFederatedId = 'owner@test.local';
|
||||
$sharedBy = 'Owner\'s name';
|
||||
$sharedByFederatedId = 'owner@test.local';
|
||||
$remote = 'https://test.local';
|
||||
$shareType = 'user';
|
||||
$roomType = Room::TYPE_GROUP;
|
||||
$roomName = 'Room name';
|
||||
$roomDefaultPermissions = Attendee::PERMISSIONS_CUSTOM | Attendee::PERMISSIONS_CHAT;
|
||||
|
||||
$shareWithUser = $this->createMock(IUser::class);
|
||||
$shareWithUserID = '10';
|
||||
|
||||
$share = new CloudFederationShare(
|
||||
$shareWith,
|
||||
$name,
|
||||
'',
|
||||
$providerId,
|
||||
$ownerFederatedId,
|
||||
$owner,
|
||||
$sharedByFederatedId,
|
||||
$sharedBy,
|
||||
$shareType,
|
||||
'talk-room',
|
||||
$token
|
||||
);
|
||||
$share->setProtocol([
|
||||
'name' => 'nctalk',
|
||||
'roomType' => $roomType,
|
||||
'roomName' => $roomName,
|
||||
'roomDefaultPermissions' => $roomDefaultPermissions,
|
||||
'options' => [
|
||||
'sharedSecret' => $token,
|
||||
],
|
||||
'invitedCloudId' => 'test@remote.test.local',
|
||||
]);
|
||||
|
||||
$invite = Invitation::fromRow(['id' => 20]);
|
||||
|
||||
// Test receiving federation expectations
|
||||
$this->federationManager->expects($this->once())
|
||||
->method('addRemoteRoom')
|
||||
->with($shareWithUser, $providerId, $roomType, $roomName, $roomDefaultPermissions, $name, $remote, $token)
|
||||
->willReturn($invite);
|
||||
|
||||
$this->config->method('isFederationEnabled')
|
||||
->willReturn(true);
|
||||
|
||||
$this->appConfig->method('getAppValueBool')
|
||||
->willReturnMap([
|
||||
['federation_incoming_enabled', true, false, true],
|
||||
]);
|
||||
|
||||
$this->config->method('isDisabledForUser')
|
||||
->with($shareWithUser)
|
||||
->willReturn(false);
|
||||
|
||||
$this->config->method('isFederationEnabledForUserId')
|
||||
->with($shareWithUser)
|
||||
->willReturn(true);
|
||||
|
||||
$this->addressHandler->expects($this->once())
|
||||
->method('splitUserRemote')
|
||||
->with($ownerFederatedId)
|
||||
->willReturn(['owner', $remote]);
|
||||
|
||||
$this->addressHandler->expects($this->once())
|
||||
->method('urlContainProtocol')
|
||||
->willReturnCallback(static fn (string $url) => str_starts_with($url, 'http://') || str_starts_with($url, 'https://'));
|
||||
|
||||
$this->userManager->expects($this->once())
|
||||
->method('get')
|
||||
->with($shareWith)
|
||||
->willReturn($shareWithUser);
|
||||
|
||||
// Test sending notification expectations
|
||||
$shareWithUser->method('getUID')
|
||||
->willReturn($shareWithUserID);
|
||||
|
||||
$notification = $this->createMock(INotification::class);
|
||||
|
||||
$notification->expects($this->once())
|
||||
->method('setApp')
|
||||
->willReturnSelf();
|
||||
|
||||
$notification->expects($this->once())
|
||||
->method('setUser')
|
||||
->with($shareWithUserID)
|
||||
->willReturnSelf();
|
||||
|
||||
$notification->expects($this->once())
|
||||
->method('setDateTime')
|
||||
->willReturnSelf();
|
||||
|
||||
$notification->expects($this->once())
|
||||
->method('setObject')
|
||||
->with('remote_talk_share', 20)
|
||||
->willReturnSelf();
|
||||
|
||||
$notification->expects($this->once())
|
||||
->method('setSubject')
|
||||
->with('remote_talk_share', [
|
||||
'sharedByDisplayName' => $sharedBy,
|
||||
'sharedByFederatedId' => $sharedByFederatedId,
|
||||
'roomName' => $roomName,
|
||||
'serverUrl' => $remote,
|
||||
'roomToken' => $name,
|
||||
]);
|
||||
|
||||
$this->notificationManager->expects($this->once())
|
||||
->method('createNotification')
|
||||
->with()
|
||||
->willReturn($notification);
|
||||
|
||||
$this->notificationManager->expects($this->once())
|
||||
->method('notify')
|
||||
->with($notification);
|
||||
|
||||
$this->assertSame('20',
|
||||
$this->cloudFederationProvider->shareReceived($share)
|
||||
);
|
||||
}
|
||||
|
||||
public function testSendAcceptNotification(): void {
|
||||
$remote = 'https://remote.test.local';
|
||||
$id = 50;
|
||||
$token = 'abcdefghijklmno';
|
||||
|
||||
$notification = $this->createMock(ICloudFederationNotification::class);
|
||||
$notification->expects($this->once())
|
||||
->method('setMessage')
|
||||
->with(
|
||||
'SHARE_ACCEPTED',
|
||||
FederationManager::TALK_ROOM_RESOURCE,
|
||||
$id,
|
||||
[
|
||||
'sharedSecret' => $token,
|
||||
'message' => 'Recipient accepted the share',
|
||||
'remoteServerUrl' => 'http://example.tld',
|
||||
'displayName' => 'Foo Bar',
|
||||
'cloudId' => 'cloudId@example.tld',
|
||||
]
|
||||
);
|
||||
|
||||
$this->cloudFederationFactory->expects($this->once())
|
||||
->method('getCloudFederationNotification')
|
||||
->with()
|
||||
->willReturn($notification);
|
||||
|
||||
$response = $this->createMock(IResponse::class);
|
||||
$response->method('getStatusCode')
|
||||
->willReturn(Http::STATUS_CREATED);
|
||||
$this->cloudFederationProviderManager->expects($this->once())
|
||||
->method('sendCloudNotification')
|
||||
->with($remote, $notification)
|
||||
->willReturn($response);
|
||||
|
||||
$this->addressHandler->method('urlContainProtocol')
|
||||
->with($remote)
|
||||
->willReturn(true);
|
||||
|
||||
$this->url->method('getAbsoluteURL')
|
||||
->with('/')
|
||||
->willReturn('http://example.tld/index.php/');
|
||||
|
||||
$success = $this->backendNotifier->sendShareAccepted($remote, $id, $token, 'Foo Bar', 'cloudId@example.tld');
|
||||
|
||||
$this->assertTrue($success);
|
||||
}
|
||||
|
||||
public function testSendRejectNotification(): void {
|
||||
$remote = 'https://remote.test.local';
|
||||
$id = 50;
|
||||
$token = 'abcdefghijklmno';
|
||||
|
||||
$notification = $this->createMock(ICloudFederationNotification::class);
|
||||
$notification->expects($this->once())
|
||||
->method('setMessage')
|
||||
->with(
|
||||
'SHARE_DECLINED',
|
||||
FederationManager::TALK_ROOM_RESOURCE,
|
||||
$id,
|
||||
[
|
||||
'sharedSecret' => $token,
|
||||
'message' => 'Recipient declined the share',
|
||||
'remoteServerUrl' => 'https://example.tld',
|
||||
]
|
||||
);
|
||||
|
||||
$this->cloudFederationFactory->expects($this->once())
|
||||
->method('getCloudFederationNotification')
|
||||
->with()
|
||||
->willReturn($notification);
|
||||
|
||||
$response = $this->createMock(IResponse::class);
|
||||
$response->method('getStatusCode')
|
||||
->willReturn(Http::STATUS_CREATED);
|
||||
$this->cloudFederationProviderManager->expects($this->once())
|
||||
->method('sendCloudNotification')
|
||||
->with($remote, $notification)
|
||||
->willReturn($response);
|
||||
|
||||
$this->addressHandler->method('urlContainProtocol')
|
||||
->with($remote)
|
||||
->willReturn(true);
|
||||
|
||||
$this->url->method('getAbsoluteURL')
|
||||
->with('/')
|
||||
->willReturn('https://example.tld/index.php/');
|
||||
|
||||
$this->backendNotifier->sendShareDeclined($remote, $id, $token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Talk\Tests\php;
|
||||
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Exceptions\GuestImportException;
|
||||
use OCA\Talk\GuestManager;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\PollService;
|
||||
use OCA\Talk\Service\RoomService;
|
||||
use OCP\Defaults;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IDateTimeZone;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserSession;
|
||||
use OCP\Mail\IMailer;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class GuestManagerTest extends TestCase {
|
||||
protected Config&MockObject $talkConfig;
|
||||
protected IMailer&MockObject $mailer;
|
||||
protected Defaults&MockObject $defaults;
|
||||
protected IUserSession&MockObject $userSession;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected PollService&MockObject $pollService;
|
||||
protected RoomService&MockObject $roomService;
|
||||
protected IURLGenerator&MockObject $urlGenerator;
|
||||
protected IL10N&MockObject $l;
|
||||
protected IEventDispatcher&MockObject $dispatcher;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
private IDateTimeZone&MockObject $dateTime;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->talkConfig = $this->createMock(Config::class);
|
||||
$this->mailer = $this->createMock(IMailer::class);
|
||||
$this->defaults = $this->createMock(Defaults::class);
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->pollService = $this->createMock(PollService::class);
|
||||
$this->roomService = $this->createMock(RoomService::class);
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
$this->l = $this->createMock(IL10N::class);
|
||||
$this->dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->dateTime = $this->createMock(IDateTimeZone::class);
|
||||
}
|
||||
|
||||
public function getGuestManager(array $methods = []): GuestManager|MockObject {
|
||||
if (!empty($methods)) {
|
||||
return $this->getMockBuilder(GuestManager::class)
|
||||
->setConstructorArgs([
|
||||
$this->talkConfig,
|
||||
$this->mailer,
|
||||
$this->defaults,
|
||||
$this->userSession,
|
||||
$this->participantService,
|
||||
$this->pollService,
|
||||
$this->roomService,
|
||||
$this->urlGenerator,
|
||||
$this->l,
|
||||
$this->dispatcher,
|
||||
$this->logger,
|
||||
$this->dateTime,
|
||||
])
|
||||
->onlyMethods($methods)
|
||||
->getMock();
|
||||
}
|
||||
|
||||
$this->guestManager = new GuestManager(
|
||||
$this->talkConfig,
|
||||
$this->mailer,
|
||||
$this->defaults,
|
||||
$this->userSession,
|
||||
$this->participantService,
|
||||
$this->pollService,
|
||||
$this->roomService,
|
||||
$this->urlGenerator,
|
||||
$this->l,
|
||||
$this->dispatcher,
|
||||
$this->logger,
|
||||
$this->dateTime,
|
||||
);
|
||||
}
|
||||
|
||||
public static function dataImportEmails(): array {
|
||||
return [
|
||||
[
|
||||
'import-valid-only-email.csv',
|
||||
1,
|
||||
0,
|
||||
[['valid@example.tld', null]],
|
||||
],
|
||||
[
|
||||
'import-valid-email-and-name.csv',
|
||||
1,
|
||||
0,
|
||||
[['valid@example.tld', 'Name']],
|
||||
],
|
||||
[
|
||||
'import-valid-filter-duplicates-by-email.csv',
|
||||
2,
|
||||
1,
|
||||
[['valid-1@example.tld', 'Valid 1'], ['valid-2@example.tld',null]],
|
||||
GuestImportException::REASON_ROWS,
|
||||
[4],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataImportEmails')]
|
||||
public function testImportEmails(string $fileName, int $invites, int $duplicates, array $invited, ?string $reason = null, array $invalidLines = []): void {
|
||||
$this->mailer->method('validateMailAddress')
|
||||
->willReturnCallback(static fn (string $email): bool => str_starts_with($email, 'valid'));
|
||||
|
||||
$actualInvites = [];
|
||||
$this->participantService->method('inviteEmailAddress')
|
||||
->willReturnCallback(function ($room, $actorId, string $email, ?string $name) use (&$actualInvites): Participant {
|
||||
$actualInvites[] = [$email, $name];
|
||||
return $this->createMock(Participant::class);
|
||||
});
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
|
||||
try {
|
||||
$guestManager = $this->getGuestManager(['sendEmailInvitation']);
|
||||
$data = $guestManager->importEmails($room, __DIR__ . '/data/' . $fileName, false);
|
||||
} catch (GuestImportException $e) {
|
||||
if ($reason === null) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$data = $e->getData();
|
||||
$this->assertSame($invalidLines, $data['invalidLines']);
|
||||
}
|
||||
|
||||
$this->assertSame($invited, $actualInvites);
|
||||
$this->assertSame($invites, $data['invites'], 'Invites count mismatch');
|
||||
$this->assertSame($duplicates, $data['duplicates'], 'Duplicates count mismatch');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,844 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Listener;
|
||||
|
||||
use OCA\DAV\CalDAV\TimezoneService;
|
||||
use OCA\Talk\Events\ACallEndedEvent;
|
||||
use OCA\Talk\Exceptions\ParticipantNotFoundException;
|
||||
use OCA\Talk\Exceptions\RoomNotFoundException;
|
||||
use OCA\Talk\Listener\CalDavEventListener;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\RoomService;
|
||||
use OCA\Talk\Webinary;
|
||||
use OCP\Calendar\Events\CalendarObjectCreatedEvent;
|
||||
use OCP\Calendar\Events\CalendarObjectDeletedEvent;
|
||||
use OCP\Calendar\Events\CalendarObjectUpdatedEvent;
|
||||
use OCP\IL10N;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class CalDavEventListenerTest extends TestCase {
|
||||
private Manager&MockObject $manager;
|
||||
private RoomService&MockObject $roomService;
|
||||
private LoggerInterface&MockObject $logger;
|
||||
private TimezoneService&MockObject $timezoneService;
|
||||
private ParticipantService&MockObject $participantService;
|
||||
private string $calData;
|
||||
private Participant&MockObject $participant;
|
||||
private string $userId;
|
||||
private string $userUri;
|
||||
private IL10N&MockObject $l10n;
|
||||
private CalDavEventListener $listener;
|
||||
|
||||
public static function roomUrl() {
|
||||
return [
|
||||
['http://talk.example.com/call/12345'],
|
||||
['http://talk.example.com/call/12345#message_789456'],
|
||||
['http://talk.example.com/call/12345#?message_789456'],
|
||||
['http://talk.example.com/call/12345?email=test@example.tld'],
|
||||
['http://talk.example.com/call/12345?email=test@example.tld#message_789456'],
|
||||
['http://talk.example.com/call/12345?email=test@example.tld#message_789456?email=test@example.tld'],
|
||||
];
|
||||
}
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->manager = $this->createMock(Manager::class);
|
||||
$this->roomService = $this->createMock(RoomService::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->timezoneService = $this->createMock(TimezoneService::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->userId = '123';
|
||||
$this->userUri = 'principals/users/' . $this->userId;
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->calData = <<<EOD
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//IDN nextcloud.com//Calendar app 5.2.0-dev.1//EN
|
||||
CALSCALE:GREGORIAN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
CREATED:20250310T171800Z
|
||||
DTSTAMP:20250310T171819Z
|
||||
LAST-MODIFIED:20250310T171819Z
|
||||
SEQUENCE:2
|
||||
UID:4d336aa1-a29e-4015-b1dd-98e1dae802db
|
||||
DTSTART;TZID=Europe/Vienna:20250314T100000
|
||||
DTEND;TZID=Europe/Vienna:20250314T110000
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Test
|
||||
LOCATION:{{{LOCATION}}}
|
||||
END:VEVENT
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Vienna
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
END:VCALENDAR
|
||||
EOD;
|
||||
|
||||
$attendee = new Attendee();
|
||||
$attendee->setParticipantType(Participant::OWNER);
|
||||
$this->participant = $this->createMock(Participant::class);
|
||||
$this->participant->method('getAttendee')->willReturn($attendee);
|
||||
|
||||
$this->listener = new CalDavEventListener(
|
||||
$this->manager,
|
||||
$this->roomService,
|
||||
$this->logger,
|
||||
$this->timezoneService,
|
||||
$this->participantService,
|
||||
$this->l10n,
|
||||
);
|
||||
}
|
||||
|
||||
public function testIsNotCalendarEvent(): void {
|
||||
$event = $this->createMock(ACallEndedEvent::class);
|
||||
$this->manager->expects(self::never())
|
||||
->method('getRoomForUserByToken');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
$this->logger->expects(self::never())
|
||||
->method('debug');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setObject');
|
||||
$this->participantService->expects(self::never())
|
||||
->method('getParticipant');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('hasExistingCalendarEvents');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testIsCalendarEventNoPrincipal(): void {
|
||||
$event = new CalendarObjectCreatedEvent(1, [], [], ['calendardata' => 'justSomeData']);
|
||||
|
||||
$this->logger->expects(self::once())
|
||||
->method('debug')
|
||||
->with('No principal uri for the event, skipping for calendar event integration');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testIsCalendarEventSystemCalendar(): void {
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => 'principals/system/system'], [], ['calendardata' => 'justSomeData']);
|
||||
|
||||
$this->logger->expects(self::once())
|
||||
->method('debug')
|
||||
->with('System calendar, skipping for calendar event integration');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testIsCalendarEventNoEventInVObject(): void {
|
||||
$calData = <<<EOD
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//IDN nextcloud.com//Something fancy//EN
|
||||
CALSCALE:GREGORIAN
|
||||
VERSION:2.0
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Paris
|
||||
X-LIC-LOCATION:Europe/Paris
|
||||
END:VTIMEZONE
|
||||
BEGIN:VTODO
|
||||
CREATED:20250310T171800Z
|
||||
DTSTAMP:20250310T171819Z
|
||||
LAST-MODIFIED:20250310T171819Z
|
||||
UID:4d336aa1-a29e-4015-b1dd-98e1dae802db
|
||||
DTSTART;TZID=Europe/Vienna:20250314T100000
|
||||
DUE;TZID=Europe/Vienna:20250314T110000
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Test
|
||||
END:VTODO
|
||||
END:VCALENDAR
|
||||
EOD;
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
|
||||
$this->logger->expects(self::once())
|
||||
->method('debug')
|
||||
->with('Calendar object is not an event, skipping for calendar event integration');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testIsCalendarEventNoData(): void {
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], []);
|
||||
|
||||
$this->logger->expects(self::once())
|
||||
->method('debug')
|
||||
->with('No calendar data for the event, skipping for calendar event integration');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testIsCalendarEventNoLocation(): void {
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => 'justSomeData']);
|
||||
|
||||
$this->logger->expects(self::once())
|
||||
->method('debug');
|
||||
$this->manager->expects(self::never())
|
||||
->method('getRoomForUserByToken');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setObject');
|
||||
$this->participantService->expects(self::never())
|
||||
->method('getParticipant');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('hasExistingCalendarEvents');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testIsCalendarEventInvalidCalendarData(): void {
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => 'justSomeData\nLOCATION:']);
|
||||
|
||||
$this->logger->expects(self::once())
|
||||
->method('warning');
|
||||
$this->manager->expects(self::never())
|
||||
->method('getRoomForUserByToken');
|
||||
$this->logger->expects(self::never())
|
||||
->method('debug');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setObject');
|
||||
$this->participantService->expects(self::never())
|
||||
->method('getParticipant');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('hasExistingCalendarEvents');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testNoUrlInLocation(): void {
|
||||
$calData = <<<EOD
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//IDN nextcloud.com//Calendar app 5.2.0-dev.1//EN
|
||||
CALSCALE:GREGORIAN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
CREATED:20250310T171800Z
|
||||
DTSTAMP:20250310T171819Z
|
||||
LAST-MODIFIED:20250310T171819Z
|
||||
SEQUENCE:2
|
||||
UID:4d336aa1-a29e-4015-b1dd-98e1dae802db
|
||||
DTSTART;TZID=Europe/Vienna:20250314T100000
|
||||
DTEND;TZID=Europe/Vienna:20250314T110000
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Test
|
||||
LOCATION:Donde esta la biblioteca
|
||||
END:VEVENT
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Vienna
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
END:VCALENDAR
|
||||
EOD;
|
||||
$event = new CalendarObjectUpdatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
|
||||
$this->logger->expects(self::once())
|
||||
->method('debug');
|
||||
$this->manager->expects(self::never())
|
||||
->method('getRoomForUserByToken');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setObject');
|
||||
$this->participantService->expects(self::never())
|
||||
->method('getParticipant');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('hasExistingCalendarEvents');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
#[DataProvider('roomUrl')]
|
||||
public function testRoomNotFound(string $roomUrl): void {
|
||||
$calData = str_replace('{{{LOCATION}}}', $roomUrl, $this->calData);
|
||||
$event = new CalendarObjectUpdatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken')
|
||||
->willThrowException(new RoomNotFoundException());
|
||||
$this->logger->expects(self::once())
|
||||
->method('warning');
|
||||
$this->logger->expects(self::never())
|
||||
->method('debug');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setObject');
|
||||
$this->participantService->expects(self::never())
|
||||
->method('getParticipant');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('hasExistingCalendarEvents');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
#[DataProvider('roomUrl')]
|
||||
public function testUserNotParticipant(string $roomUrl): void {
|
||||
$calData = str_replace('{{{LOCATION}}}', $roomUrl, $this->calData);
|
||||
$event = new CalendarObjectUpdatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken');
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willThrowException(new ParticipantNotFoundException());
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
$this->logger->expects(self::once())
|
||||
->method('debug');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setObject');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('hasExistingCalendarEvents');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
#[DataProvider('roomUrl')]
|
||||
public function testUserNotOwner(string $roomUrl): void {
|
||||
$calData = str_replace('{{{LOCATION}}}', $roomUrl, $this->calData);
|
||||
$event = new CalendarObjectUpdatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
$attendee = new Attendee();
|
||||
$attendee->setParticipantType(Participant::USER);
|
||||
$participant = $this->createMock(Participant::class);
|
||||
$participant->method('getAttendee')->willReturn($attendee);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken');
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willReturn($participant);
|
||||
$this->logger->expects(self::once())
|
||||
->method('debug')
|
||||
->with("Participant $this->userId is not owner for calendar event integration");
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setObject');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('hasExistingCalendarEvents');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
#[DataProvider('roomUrl')]
|
||||
public function testRoomNotEventRoom(string $roomUrl): void {
|
||||
$calData = str_replace('{{{LOCATION}}}', $roomUrl, $this->calData);
|
||||
$event = new CalendarObjectUpdatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getObjectType')->willReturn(Room::OBJECT_TYPE_PHONE_LEGACY);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken')
|
||||
->willReturn($room);
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willReturn($this->participant);
|
||||
$this->logger->expects(self::once())
|
||||
->method('debug');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setObject');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('hasExistingCalendarEvents');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testEventHasRRULE(): void {
|
||||
$calData = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//IDN nextcloud.com//Calendar app 5.2.0-dev.1//EN
|
||||
CALSCALE:GREGORIAN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
CREATED:20250310T175122Z
|
||||
DTSTAMP:20250310T175146Z
|
||||
LAST-MODIFIED:20250310T175146Z
|
||||
SEQUENCE:2
|
||||
UID:2fb2416e-13f3-4945-936e-28df560b00a2
|
||||
DTSTART;TZID=Europe/Vienna:20250315T100000
|
||||
DTEND;TZID=Europe/Vienna:20250315T110000
|
||||
STATUS:CONFIRMED
|
||||
LOCATION:https://nextcloud.local/index.php/call/44wd9tvp
|
||||
RRULE:FREQ=DAILY;UNTIL=20250322T090000Z
|
||||
DESCRIPTION:Test
|
||||
END:VEVENT
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Vienna
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
END:VCALENDAR
|
||||
EOF;
|
||||
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getObjectType')->willReturn(Room::OBJECT_TYPE_EVENT);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken')
|
||||
->willReturn($room);
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willReturn($this->participant);
|
||||
$this->roomService->expects(self::once())
|
||||
->method('resetObject')
|
||||
->with($room);
|
||||
$this->roomService->expects(self::once())
|
||||
->method('setDescription')
|
||||
->with($room, 'Test');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setObject');
|
||||
$this->logger->expects(self::once())
|
||||
->method('debug');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->roomService->expects(self::never())
|
||||
->method('hasExistingCalendarEvents');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
#[DataProvider('roomUrl')]
|
||||
public function testHasExistingRooms(string $roomUrl): void {
|
||||
$calData = str_replace('{{{LOCATION}}}', $roomUrl, $this->calData);
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getObjectType')->willReturn(Room::OBJECT_TYPE_EVENT);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken')
|
||||
->willReturn($room);
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willReturn($this->participant);
|
||||
$this->roomService->expects(self::once())
|
||||
->method('hasExistingCalendarEvents')
|
||||
->willReturn(true);
|
||||
$this->roomService->expects(self::once())
|
||||
->method('resetObject')
|
||||
->with($room);
|
||||
$this->roomService->expects(self::never())
|
||||
->method('setObject');
|
||||
$this->logger->expects(self::once())
|
||||
->method('debug');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
#[DataProvider('roomUrl')]
|
||||
public function testDeletedEvents(string $roomUrl): void {
|
||||
$calData = str_replace('{{{LOCATION}}}', $roomUrl, $this->calData);
|
||||
$event = new CalendarObjectDeletedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getObjectType')->willReturn(Room::OBJECT_TYPE_EVENT);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken')
|
||||
->willReturn($room);
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willReturn($this->participant);
|
||||
$this->roomService->expects(self::once())
|
||||
->method('hasExistingCalendarEvents')
|
||||
->willReturn(false);
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::once())
|
||||
->method('setReadOnly')
|
||||
->with($room, Room::READ_ONLY);
|
||||
// $this->roomService->expects(self::never())
|
||||
// ->method('setLobby');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->logger->expects(self::never())
|
||||
->method('debug');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
#[DataProvider('roomUrl')]
|
||||
public function testTime(string $roomUrl): void {
|
||||
$calData = str_replace('{{{LOCATION}}}', $roomUrl, $this->calData);
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getObjectType')->willReturn(Room::OBJECT_TYPE_EVENT);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken')
|
||||
->willReturn($room);
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willReturn($this->participant);
|
||||
$this->roomService->expects(self::once())
|
||||
->method('hasExistingCalendarEvents')
|
||||
->willReturn(false);
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::once())
|
||||
->method('setObject')
|
||||
->with($room, Room::OBJECT_TYPE_EVENT, '1741942800#1741946400');
|
||||
// $this->roomService->expects(self::once())
|
||||
// ->method('setLobby')
|
||||
// ->with($room, Webinary::LOBBY_NON_MODERATORS, null);
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->logger->expects(self::never())
|
||||
->method('debug');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testTimezone(): void {
|
||||
$calData = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//IDN nextcloud.com//Calendar app 5.2.0-dev.1//EN
|
||||
CALSCALE:GREGORIAN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
CREATED:20250310T180746Z
|
||||
DTSTAMP:20250310T180758Z
|
||||
LAST-MODIFIED:20250310T180758Z
|
||||
SEQUENCE:2
|
||||
UID:75847de7-3754-4aae-87a4-f03755163b66
|
||||
DTSTART;VALUE=DATE:20250313
|
||||
DTEND;VALUE=DATE:20250314
|
||||
STATUS:CONFIRMED
|
||||
LOCATION:https://nextcloud.local/index.php/call/jpmrumps
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
EOF;
|
||||
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getObjectType')->willReturn(Room::OBJECT_TYPE_EVENT);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken')
|
||||
->willReturn($room);
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willReturn($this->participant);
|
||||
$this->roomService->expects(self::once())
|
||||
->method('hasExistingCalendarEvents')
|
||||
->willReturn(false);
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::once())
|
||||
->method('setObject')
|
||||
->with($room, Room::OBJECT_TYPE_EVENT, '1741820400#1741906800');
|
||||
// $this->roomService->expects(self::once())
|
||||
// ->method('setLobby')
|
||||
// ->with($room, Webinary::LOBBY_NON_MODERATORS, null);
|
||||
$this->timezoneService->expects(self::once())
|
||||
->method('getUserTimezone')
|
||||
->willReturn('Europe/Vienna');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->logger->expects(self::never())
|
||||
->method('debug');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testTimezoneDefaultFallback(): void {
|
||||
$calData = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//IDN nextcloud.com//Calendar app 5.2.0-dev.1//EN
|
||||
CALSCALE:GREGORIAN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
CREATED:20250310T180746Z
|
||||
DTSTAMP:20250310T180758Z
|
||||
LAST-MODIFIED:20250310T180758Z
|
||||
SEQUENCE:2
|
||||
UID:75847de7-3754-4aae-87a4-f03755163b66
|
||||
DTSTART;VALUE=DATE:20250313
|
||||
DTEND;VALUE=DATE:20250314
|
||||
STATUS:CONFIRMED
|
||||
LOCATION:https://nextcloud.local/index.php/call/jpmrumps
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
EOF;
|
||||
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getObjectType')->willReturn(Room::OBJECT_TYPE_EVENT);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken')
|
||||
->willReturn($room);
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willReturn($this->participant);
|
||||
$this->roomService->expects(self::once())
|
||||
->method('hasExistingCalendarEvents')
|
||||
->willReturn(false);
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::once())
|
||||
->method('setObject')
|
||||
->with($room, Room::OBJECT_TYPE_EVENT, '1741820400#1741906800');
|
||||
// $this->roomService->expects(self::once())
|
||||
// ->method('setLobby')
|
||||
// ->with($room, Webinary::LOBBY_NON_MODERATORS, null);
|
||||
$this->timezoneService->expects(self::once())
|
||||
->method('getUserTimezone')
|
||||
->willReturn(null);
|
||||
$this->timezoneService->expects(self::once())
|
||||
->method('getDefaultTimezone')
|
||||
->willReturn('Europe/Vienna');
|
||||
$this->logger->expects(self::never())
|
||||
->method('debug');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testTimezoneUTC(): void {
|
||||
$calData = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//IDN nextcloud.com//Calendar app 5.2.0-dev.1//EN
|
||||
CALSCALE:GREGORIAN
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
CREATED:20250310T180746Z
|
||||
DTSTAMP:20250310T180758Z
|
||||
LAST-MODIFIED:20250310T180758Z
|
||||
SEQUENCE:2
|
||||
UID:75847de7-3754-4aae-87a4-f03755163b66
|
||||
DTSTART;VALUE=DATE:20250313
|
||||
DTEND;VALUE=DATE:20250314
|
||||
STATUS:CONFIRMED
|
||||
LOCATION:https://nextcloud.local/index.php/call/jpmrumps
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
EOF;
|
||||
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getObjectType')->willReturn(Room::OBJECT_TYPE_EVENT);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken')
|
||||
->willReturn($room);
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willReturn($this->participant);
|
||||
$this->roomService->expects(self::once())
|
||||
->method('hasExistingCalendarEvents')
|
||||
->willReturn(false);
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::once())
|
||||
->method('setObject')
|
||||
->with($room, Room::OBJECT_TYPE_EVENT, '1741824000#1741910400');
|
||||
$this->timezoneService->expects(self::once())
|
||||
->method('getUserTimezone')
|
||||
->willReturn(null);
|
||||
$this->timezoneService->expects(self::once())
|
||||
->method('getDefaultTimezone')
|
||||
->willReturn('Garbage');
|
||||
$this->logger->expects(self::never())
|
||||
->method('debug');
|
||||
$this->logger->expects(self::once())
|
||||
->method('warning');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testSummaryWithSurroundingWhitespaceGetsTrimmed(): void {
|
||||
$calData = str_replace(
|
||||
'SUMMARY:Test',
|
||||
'SUMMARY: Test Meeting ',
|
||||
str_replace('{{{LOCATION}}}', 'http://talk.example.com/call/12345', $this->calData)
|
||||
);
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getObjectType')->willReturn(Room::OBJECT_TYPE_EVENT);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken')
|
||||
->willReturn($room);
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willReturn($this->participant);
|
||||
$this->roomService->expects(self::once())
|
||||
->method('hasExistingCalendarEvents')
|
||||
->willReturn(false);
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::once())
|
||||
->method('setName')
|
||||
->with($room, 'Test Meeting');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->logger->expects(self::never())
|
||||
->method('debug');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testWhitespaceOnlySummaryBecomesEmptyString(): void {
|
||||
$calData = str_replace(
|
||||
'SUMMARY:Test',
|
||||
'SUMMARY: ',
|
||||
str_replace('{{{LOCATION}}}', 'http://talk.example.com/call/12345', $this->calData)
|
||||
);
|
||||
$event = new CalendarObjectCreatedEvent(1, ['principaluri' => $this->userUri], [], ['calendardata' => $calData]);
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getObjectType')->willReturn(Room::OBJECT_TYPE_EVENT);
|
||||
|
||||
$this->manager->expects(self::once())
|
||||
->method('getRoomForUserByToken')
|
||||
->willReturn($room);
|
||||
$this->participantService->expects(self::once())
|
||||
->method('getParticipant')
|
||||
->willReturn($this->participant);
|
||||
$this->roomService->expects(self::once())
|
||||
->method('hasExistingCalendarEvents')
|
||||
->willReturn(false);
|
||||
$this->roomService->expects(self::never())
|
||||
->method('resetObject');
|
||||
$this->roomService->expects(self::once())
|
||||
->method('setName')
|
||||
->with($room, '');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getUserTimezone');
|
||||
$this->timezoneService->expects(self::never())
|
||||
->method('getDefaultTimezone');
|
||||
$this->logger->expects(self::never())
|
||||
->method('debug');
|
||||
$this->logger->expects(self::never())
|
||||
->method('warning');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Listener;
|
||||
|
||||
use OCA\Talk\Events\BeforeParticipantModifiedEvent;
|
||||
use OCA\Talk\Exceptions\ForbiddenException;
|
||||
use OCA\Talk\Listener\RestrictStartingCalls;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCP\IConfig;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class RestrictStartingCallsTest extends TestCase {
|
||||
protected IConfig&MockObject $serverConfig;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected ?RestrictStartingCalls $listener = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->serverConfig = $this->createMock(IConfig::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->listener = new RestrictStartingCalls($this->serverConfig, $this->participantService);
|
||||
}
|
||||
|
||||
public static function dataCheckStartCallPermissions(): array {
|
||||
return [
|
||||
'default blocked' => [Room::TYPE_PUBLIC, '', false, false, true],
|
||||
|
||||
'allowed password request' => [Room::TYPE_PUBLIC, Room::OBJECT_TYPE_VIDEO_VERIFICATION, false, false, false],
|
||||
'call active already' => [Room::TYPE_PUBLIC, '', false, true, false],
|
||||
'user has permissions' => [Room::TYPE_PUBLIC, '', true, false, false],
|
||||
'user has permissions & call' => [Room::TYPE_PUBLIC, '', true, true, false],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataCheckStartCallPermissions')]
|
||||
public function testCheckStartCallPermissions(int $roomType, string $roomObjectType, bool $canStart, bool $hasParticipants, bool $throws): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getType')
|
||||
->willReturn($roomType);
|
||||
$room->method('getObjectType')
|
||||
->willReturn($roomObjectType);
|
||||
|
||||
$participant = $this->createMock(Participant::class);
|
||||
$participant->method('canStartCall')
|
||||
->with($this->serverConfig)
|
||||
->willReturn($canStart);
|
||||
|
||||
$this->participantService->method('hasActiveSessionsInCall')
|
||||
->willReturn($hasParticipants);
|
||||
|
||||
$event = new BeforeParticipantModifiedEvent(
|
||||
$room,
|
||||
$participant,
|
||||
'inCall',
|
||||
Participant::FLAG_IN_CALL,
|
||||
Participant::FLAG_DISCONNECTED
|
||||
);
|
||||
|
||||
if ($throws) {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
}
|
||||
|
||||
$this->overwriteService(RestrictStartingCalls::class, $this->listener);
|
||||
$this->listener->handle($event);
|
||||
$this->restoreService(RestrictStartingCalls::class);
|
||||
|
||||
if (!$throws) {
|
||||
self::assertTrue(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php;
|
||||
|
||||
use OC\Authentication\Token\IProvider;
|
||||
use OCA\Talk\Chat\ChatManager;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\MatterbridgeManager;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IAvatarManager;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Security\IRemoteHostValidator;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group(name: 'DB')]
|
||||
class MatterbridgeManagerTest extends TestCase {
|
||||
protected IDBConnection&MockObject $db;
|
||||
protected IConfig&MockObject $config;
|
||||
protected IURLGenerator&MockObject $url;
|
||||
protected IUserManager&MockObject $userManager;
|
||||
protected Manager&MockObject $manager;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected ChatManager&MockObject $chatManager;
|
||||
protected IProvider&MockObject $tokenProvider;
|
||||
protected ISecureRandom&MockObject $random;
|
||||
protected IAvatarManager&MockObject $avatarManager;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected IRemoteHostValidator&MockObject $hostValidator;
|
||||
protected MatterbridgeManager $matterbridgeManager;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->db = $this->createMock(IDBConnection::class);
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->url = $this->createMock(IURLGenerator::class);
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->manager = $this->createMock(Manager::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->chatManager = $this->createMock(ChatManager::class);
|
||||
$this->tokenProvider = $this->createMock(IProvider::class);
|
||||
$this->random = $this->createMock(ISecureRandom::class);
|
||||
$this->avatarManager = $this->createMock(IAvatarManager::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->hostValidator = $this->createMock(IRemoteHostValidator::class);
|
||||
|
||||
$this->matterbridgeManager = new MatterbridgeManager(
|
||||
$this->db,
|
||||
$this->config,
|
||||
$this->url,
|
||||
$this->userManager,
|
||||
$this->manager,
|
||||
$this->participantService,
|
||||
$this->chatManager,
|
||||
$this->tokenProvider,
|
||||
$this->random,
|
||||
$this->avatarManager,
|
||||
$this->logger,
|
||||
$this->timeFactory,
|
||||
$this->hostValidator,
|
||||
);
|
||||
}
|
||||
|
||||
public static function dataValidateParts(): array {
|
||||
return [
|
||||
'Only strings allowed' => [
|
||||
[['type' => false]],
|
||||
[],
|
||||
true,
|
||||
],
|
||||
'Only strings allowed unless editing' => [
|
||||
[['type' => 'other', 'editing' => true]],
|
||||
[],
|
||||
false,
|
||||
],
|
||||
'Mattermost - Host only' => [
|
||||
[['type' => 'mattermost', 'server' => 'yourmattermostserver.example.tld']],
|
||||
[['yourmattermostserver.example.tld', true]],
|
||||
false,
|
||||
],
|
||||
'IRC - With port' => [
|
||||
[['type' => 'irc', 'server' => 'irc.example.tld:6667']],
|
||||
[['irc.example.tld', true]],
|
||||
false,
|
||||
],
|
||||
'Rocketchat - Full' => [
|
||||
[['type' => 'rocketchat', 'server' => 'https://yourrocketchatserver.example.tld:443']],
|
||||
[['yourrocketchatserver.example.tld', true]],
|
||||
false,
|
||||
],
|
||||
'Rocketchat - Internal' => [
|
||||
[['type' => 'rocketchat', 'server' => 'https://localhost']],
|
||||
[['localhost', false]],
|
||||
true,
|
||||
],
|
||||
'Talk' => [
|
||||
[['type' => 'nctalk', 'server' => 'https://cloud.example2.tld']],
|
||||
[['cloud.example2.tld', true]],
|
||||
false,
|
||||
],
|
||||
'Talk - Internal' => [
|
||||
[['type' => 'nctalk', 'server' => 'https://cloud.example.tld']],
|
||||
[['cloud.example.tld', false]],
|
||||
false,
|
||||
],
|
||||
'Talk - Internal port scan' => [
|
||||
[['type' => 'nctalk', 'server' => 'https://cloud.example.tld:8080']],
|
||||
[['cloud.example.tld', false]],
|
||||
true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataValidateParts')]
|
||||
public function testValidateParts(array $parts, array $hostValidatorData, bool $throws): void {
|
||||
$this->hostValidator->method('isValid')
|
||||
->willReturnMap($hostValidatorData);
|
||||
$this->url->method('getAbsoluteURL')
|
||||
->willReturn('https://cloud.example.tld/');
|
||||
|
||||
if ($throws) {
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
self::invokePrivate($this->matterbridgeManager, 'validateParts', [$parts]);
|
||||
} else {
|
||||
$this->assertEquals($parts, self::invokePrivate($this->matterbridgeManager, 'validateParts', [$parts]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Mocks;
|
||||
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
|
||||
class GetTurnServerListener implements IEventListener {
|
||||
public function handle(Event $event): void {
|
||||
$event->setServers([
|
||||
[
|
||||
'schemes' => 'turn',
|
||||
'server' => 'turn.domain.invalid',
|
||||
'username' => 'john',
|
||||
'password' => 'abcde',
|
||||
'protocols' => 'udp,tcp',
|
||||
],
|
||||
[
|
||||
'schemes' => 'turns',
|
||||
'server' => 'turns.domain.invalid',
|
||||
'username' => 'jane',
|
||||
'password' => 'ABCDE',
|
||||
'protocols' => 'tcp',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Model;
|
||||
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Model\AttendeeMapper;
|
||||
use OCA\Talk\Participant;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\IDBConnection;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class AttendeeMapperTest extends TestCase {
|
||||
protected ?AttendeeMapper $attendeeMapper = null;
|
||||
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->attendeeMapper = new AttendeeMapper(
|
||||
\OCP\Server::get(IDBConnection::class)
|
||||
);
|
||||
}
|
||||
|
||||
public static function dataModifyPermissions(): array {
|
||||
return [
|
||||
0 => [
|
||||
[
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_CIRCLES,
|
||||
'actor_id' => 'c1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_GROUPS,
|
||||
'actor_id' => 'g1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'o1',
|
||||
'participant_type' => Participant::OWNER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'm1',
|
||||
'participant_type' => Participant::MODERATOR,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
],
|
||||
Attendee::PERMISSIONS_MODIFY_SET,
|
||||
Attendee::PERMISSIONS_CALL_START,
|
||||
[
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_CIRCLES,
|
||||
'actor_id' => 'c1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_GROUPS,
|
||||
'actor_id' => 'g1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'o1',
|
||||
'participant_type' => Participant::OWNER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'm1',
|
||||
'participant_type' => Participant::MODERATOR,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
],
|
||||
],
|
||||
1 => [
|
||||
[
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_CIRCLES,
|
||||
'actor_id' => 'c1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_GROUPS,
|
||||
'actor_id' => 'g1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'o1',
|
||||
'participant_type' => Participant::OWNER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'm1',
|
||||
'participant_type' => Participant::MODERATOR,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
],
|
||||
Attendee::PERMISSIONS_MODIFY_SET,
|
||||
Attendee::PERMISSIONS_CALL_START,
|
||||
[
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_CIRCLES,
|
||||
'actor_id' => 'c1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_GROUPS,
|
||||
'actor_id' => 'g1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'o1',
|
||||
'participant_type' => Participant::OWNER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'm1',
|
||||
'participant_type' => Participant::MODERATOR,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
],
|
||||
],
|
||||
2 => [
|
||||
[
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'o1',
|
||||
'participant_type' => Participant::OWNER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO + Attendee::PERMISSIONS_PUBLISH_VIDEO,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'm1',
|
||||
'participant_type' => Participant::MODERATOR,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO + Attendee::PERMISSIONS_PUBLISH_VIDEO,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO + Attendee::PERMISSIONS_PUBLISH_VIDEO,
|
||||
],
|
||||
],
|
||||
Attendee::PERMISSIONS_MODIFY_SET,
|
||||
Attendee::PERMISSIONS_CALL_START,
|
||||
[
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'o1',
|
||||
'participant_type' => Participant::OWNER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'm1',
|
||||
'participant_type' => Participant::MODERATOR,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
],
|
||||
],
|
||||
3 => [
|
||||
[
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'o1',
|
||||
'participant_type' => Participant::OWNER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO + Attendee::PERMISSIONS_PUBLISH_VIDEO,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'm1',
|
||||
'participant_type' => Participant::MODERATOR,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_VIDEO,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u2',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
],
|
||||
Attendee::PERMISSIONS_MODIFY_ADD,
|
||||
Attendee::PERMISSIONS_CALL_START,
|
||||
[
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'o1',
|
||||
'participant_type' => Participant::OWNER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO + Attendee::PERMISSIONS_PUBLISH_VIDEO + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'm1',
|
||||
'participant_type' => Participant::MODERATOR,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_VIDEO + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u2',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
],
|
||||
],
|
||||
4 => [
|
||||
[
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'o1',
|
||||
'participant_type' => Participant::OWNER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO + Attendee::PERMISSIONS_PUBLISH_VIDEO + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'm1',
|
||||
'participant_type' => Participant::MODERATOR,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_VIDEO + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u2',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_CALL_START,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u3',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO + Attendee::PERMISSIONS_PUBLISH_VIDEO,
|
||||
],
|
||||
],
|
||||
Attendee::PERMISSIONS_MODIFY_REMOVE,
|
||||
Attendee::PERMISSIONS_CALL_START,
|
||||
[
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'o1',
|
||||
'participant_type' => Participant::OWNER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO + Attendee::PERMISSIONS_PUBLISH_VIDEO,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'm1',
|
||||
'participant_type' => Participant::MODERATOR,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_VIDEO,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u1',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u2',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM,
|
||||
],
|
||||
[
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'u3',
|
||||
'participant_type' => Participant::USER,
|
||||
'permissions' => Attendee::PERMISSIONS_CUSTOM + Attendee::PERMISSIONS_PUBLISH_AUDIO + Attendee::PERMISSIONS_PUBLISH_VIDEO,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataModifyPermissions')]
|
||||
public function testModifyPermissions(array $attendees, string $mode, int $permission, array $expected): void {
|
||||
$roomId = 12345678;
|
||||
|
||||
foreach ($attendees as $attendeeData) {
|
||||
try {
|
||||
$attendee = $this->attendeeMapper->findByActor($roomId, $attendeeData['actor_type'], $attendeeData['actor_id']);
|
||||
$this->attendeeMapper->delete($attendee);
|
||||
} catch (DoesNotExistException $e) {
|
||||
}
|
||||
|
||||
$attendee = new Attendee();
|
||||
$attendee->setRoomId($roomId);
|
||||
$attendee->setActorType($attendeeData['actor_type']);
|
||||
$attendee->setActorId($attendeeData['actor_id']);
|
||||
$attendee->setParticipantType($attendeeData['participant_type']);
|
||||
$attendee->setPermissions($attendeeData['permissions']);
|
||||
$this->attendeeMapper->insert($attendee);
|
||||
}
|
||||
|
||||
$this->attendeeMapper->modifyPermissions($roomId, $mode, $permission);
|
||||
|
||||
foreach ($expected as $attendeeData) {
|
||||
$attendee = $this->attendeeMapper->findByActor($roomId, $attendeeData['actor_type'], $attendeeData['actor_id']);
|
||||
|
||||
$this->assertEquals(
|
||||
$attendeeData['permissions'],
|
||||
$attendee->getPermissions(),
|
||||
'Permissions mismatch for ' . $attendeeData['actor_type'] . '#' . $attendeeData['actor_id']
|
||||
);
|
||||
$this->attendeeMapper->delete($attendee);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Recording;
|
||||
|
||||
use OCA\Talk\Chat\CommentsManager;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Federation\Authenticator;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\AttendeeMapper;
|
||||
use OCA\Talk\Model\SessionMapper;
|
||||
use OCA\Talk\Recording\BackendNotifier;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\RoomService;
|
||||
use OCA\Talk\TalkSession;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Services\IAppConfig;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Config\IUserConfig;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Security\IHasher;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class CustomBackendNotifier extends BackendNotifier {
|
||||
private array $requests = [];
|
||||
|
||||
public function getRequests(): array {
|
||||
return $this->requests;
|
||||
}
|
||||
|
||||
public function clearRequests() {
|
||||
$this->requests = [];
|
||||
}
|
||||
|
||||
protected function doRequest(string $url, array $params, int $retries = 3): void {
|
||||
$this->requests[] = [
|
||||
'url' => $url,
|
||||
'params' => $params,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
#[Group('DB')]
|
||||
class BackendNotifierTest extends TestCase {
|
||||
protected IURLGenerator&MockObject $urlGenerator;
|
||||
protected ParticipantService $participantService;
|
||||
protected ?CustomBackendNotifier $backendNotifier = null;
|
||||
protected ?Config $config = null;
|
||||
protected ?ISecureRandom $secureRandom = null;
|
||||
protected ?Manager $manager = null;
|
||||
protected ?string $recordingSecret = null;
|
||||
protected ?string $baseUrl = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$config = \OCP\Server::get(IConfig::class);
|
||||
$this->recordingSecret = 'the-recording-secret';
|
||||
$this->baseUrl = 'https://localhost/recording';
|
||||
$config->setAppValue('spreed', 'recording_servers', json_encode([
|
||||
'secret' => $this->recordingSecret,
|
||||
'servers' => [
|
||||
[
|
||||
'server' => $this->baseUrl,
|
||||
],
|
||||
],
|
||||
]));
|
||||
|
||||
$this->secureRandom = \OCP\Server::get(ISecureRandom::class);
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
|
||||
$appConfig = $this->createMock(IAppConfig::class);
|
||||
$groupManager = $this->createMock(IGroupManager::class);
|
||||
$userManager = $this->createMock(IUserManager::class);
|
||||
$timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$dispatcher = \OCP\Server::get(IEventDispatcher::class);
|
||||
|
||||
$this->config = new Config($config, $appConfig, $this->createMock(IUserConfig::class), $this->secureRandom, $groupManager, $userManager, $this->urlGenerator, $timeFactory, $dispatcher);
|
||||
|
||||
$this->recreateBackendNotifier();
|
||||
|
||||
$this->participantService = \OCP\Server::get(ParticipantService::class);
|
||||
|
||||
$dbConnection = \OCP\Server::get(IDBConnection::class);
|
||||
$this->manager = new Manager(
|
||||
$dbConnection,
|
||||
$config,
|
||||
$this->config,
|
||||
\OCP\Server::get(IAppManager::class),
|
||||
\OCP\Server::get(AttendeeMapper::class),
|
||||
\OCP\Server::get(SessionMapper::class),
|
||||
$this->participantService,
|
||||
$this->secureRandom,
|
||||
$this->createMock(IUserManager::class),
|
||||
$groupManager,
|
||||
$this->createMock(CommentsManager::class),
|
||||
$this->createMock(TalkSession::class),
|
||||
$dispatcher,
|
||||
$timeFactory,
|
||||
$this->createMock(IHasher::class),
|
||||
$this->createMock(IL10N::class),
|
||||
$this->createMock(Authenticator::class),
|
||||
);
|
||||
}
|
||||
|
||||
public function tearDown(): void {
|
||||
$config = \OCP\Server::get(IConfig::class);
|
||||
$config->deleteAppValue('spreed', 'recording_servers');
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
private function recreateBackendNotifier() {
|
||||
$this->backendNotifier = new CustomBackendNotifier(
|
||||
$this->config,
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(IClientService::class),
|
||||
$this->secureRandom,
|
||||
$this->urlGenerator,
|
||||
);
|
||||
}
|
||||
|
||||
private function calculateBackendChecksum($data, $random) {
|
||||
if (empty($random) || strlen($random) < 32) {
|
||||
return false;
|
||||
}
|
||||
return hash_hmac('sha256', $random . $data, $this->recordingSecret);
|
||||
}
|
||||
|
||||
private function validateBackendRequest($expectedUrl, $request) {
|
||||
$this->assertTrue(isset($request));
|
||||
$this->assertEquals($expectedUrl, $request['url']);
|
||||
$headers = $request['params']['headers'];
|
||||
$this->assertEquals('application/json', $headers['Content-Type']);
|
||||
$random = $headers['Talk-Recording-Random'];
|
||||
$checksum = $headers['Talk-Recording-Checksum'];
|
||||
$body = $request['params']['body'];
|
||||
$this->assertEquals($this->calculateBackendChecksum($body, $random), $checksum);
|
||||
return $body;
|
||||
}
|
||||
|
||||
private function assertMessageWasSent(Room $room, array $message): void {
|
||||
$expectedUrl = $this->baseUrl . '/api/v1/room/' . $room->getToken();
|
||||
|
||||
$requests = $this->backendNotifier->getRequests();
|
||||
$requests = array_filter($requests, function ($request) use ($expectedUrl) {
|
||||
return $request['url'] === $expectedUrl;
|
||||
});
|
||||
$bodies = array_map(function ($request) use ($expectedUrl) {
|
||||
return json_decode($this->validateBackendRequest($expectedUrl, $request), true);
|
||||
}, $requests);
|
||||
|
||||
$bodies = array_filter($bodies, function (array $body) use ($message) {
|
||||
return $body['type'] === $message['type'];
|
||||
});
|
||||
|
||||
$this->assertContainsEquals($message, $bodies, json_encode($bodies, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
public function testStart(): void {
|
||||
$userId = 'testUser';
|
||||
|
||||
/** @var IUser&MockObject $testUser */
|
||||
$testUser = $this->createMock(IUser::class);
|
||||
$testUser->expects($this->any())
|
||||
->method('getUID')
|
||||
->willReturn($userId);
|
||||
|
||||
$roomService = $this->createMock(RoomService::class);
|
||||
$roomService->method('verifyPassword')
|
||||
->willReturn(['result' => true, 'url' => '']);
|
||||
|
||||
$room = $this->manager->createRoom(Room::TYPE_PUBLIC);
|
||||
$this->participantService->addUsers($room, [[
|
||||
'actorType' => 'users',
|
||||
'actorId' => $userId,
|
||||
]]);
|
||||
$participant = $this->participantService->joinRoom($roomService, $room, $testUser, '');
|
||||
|
||||
$this->backendNotifier->start($room, Room::RECORDING_VIDEO, 'participant1', $participant);
|
||||
|
||||
$this->assertMessageWasSent($room, [
|
||||
'type' => 'start',
|
||||
'start' => [
|
||||
'status' => Room::RECORDING_VIDEO,
|
||||
'owner' => 'participant1',
|
||||
'actor' => [
|
||||
'type' => 'users',
|
||||
'id' => $userId,
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testStop(): void {
|
||||
$userId = 'testUser';
|
||||
|
||||
/** @var IUser&MockObject $testUser */
|
||||
$testUser = $this->createMock(IUser::class);
|
||||
$testUser->expects($this->any())
|
||||
->method('getUID')
|
||||
->willReturn($userId);
|
||||
|
||||
$roomService = $this->createMock(RoomService::class);
|
||||
$roomService->method('verifyPassword')
|
||||
->willReturn(['result' => true, 'url' => '']);
|
||||
|
||||
$room = $this->manager->createRoom(Room::TYPE_PUBLIC);
|
||||
$this->participantService->addUsers($room, [[
|
||||
'actorType' => 'users',
|
||||
'actorId' => $userId,
|
||||
]]);
|
||||
$participant = $this->participantService->joinRoom($roomService, $room, $testUser, '');
|
||||
|
||||
$this->backendNotifier->stop($room, $participant);
|
||||
|
||||
$this->assertMessageWasSent($room, [
|
||||
'type' => 'stop',
|
||||
'stop' => [
|
||||
'actor' => [
|
||||
'type' => 'users',
|
||||
'id' => $userId,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->backendNotifier->stop($room);
|
||||
|
||||
$this->assertMessageWasSent($room, [
|
||||
'type' => 'stop',
|
||||
'stop' => [
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Service;
|
||||
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\AvatarService;
|
||||
use OCA\Talk\Service\EmojiService;
|
||||
use OCA\Talk\Service\RoomService;
|
||||
use OCP\Files\IAppData;
|
||||
use OCP\IAvatarManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use OCP\Server;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class AvatarServiceTest extends TestCase {
|
||||
protected IAppData&MockObject $appData;
|
||||
protected IL10N&MockObject $l;
|
||||
protected IURLGenerator&MockObject $url;
|
||||
protected ISecureRandom&MockObject $random;
|
||||
protected RoomService&MockObject $roomService;
|
||||
protected IAvatarManager&MockObject $avatarManager;
|
||||
protected EmojiService $emojiService;
|
||||
protected ?AvatarService $service = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->appData = $this->createMock(IAppData::class);
|
||||
$this->l = $this->createMock(IL10N::class);
|
||||
$this->url = $this->createMock(IURLGenerator::class);
|
||||
$this->random = $this->createMock(ISecureRandom::class);
|
||||
$this->roomService = $this->createMock(RoomService::class);
|
||||
$this->avatarManager = $this->createMock(IAvatarManager::class);
|
||||
$this->emojiService = Server::get(EmojiService::class);
|
||||
$this->service = new AvatarService(
|
||||
$this->appData,
|
||||
$this->l,
|
||||
$this->url,
|
||||
$this->random,
|
||||
$this->roomService,
|
||||
$this->avatarManager,
|
||||
$this->emojiService,
|
||||
);
|
||||
}
|
||||
|
||||
public static function dataGetAvatarVersion(): array {
|
||||
return [
|
||||
['', 'STRING WITH 8 CHARS'],
|
||||
['1', '1'],
|
||||
['1.png', '1'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataGetAvatarVersion')]
|
||||
public function testGetAvatarVersion(string $avatar, string $expected): void {
|
||||
/** @var Room&MockObject $room */
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getAvatar')
|
||||
->willReturn($avatar);
|
||||
$actual = $this->service->getAvatarVersion($room);
|
||||
if ($expected === 'STRING WITH 8 CHARS') {
|
||||
$this->assertEquals(8, strlen($actual));
|
||||
} else {
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Service;
|
||||
|
||||
use OCA\Talk\Chat\ChatManager;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Service\BreakoutRoomService;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\RoomService;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IL10N;
|
||||
use OCP\Notification\IManager as INotificationManager;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class BreakoutRoomServiceTest extends TestCase {
|
||||
protected Config&MockObject $config;
|
||||
protected Manager&MockObject $manager;
|
||||
protected RoomService&MockObject $roomService;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected ChatManager&MockObject $chatManager;
|
||||
protected INotificationManager&MockObject $notificationManager;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected IEventDispatcher&MockObject $dispatcher;
|
||||
protected IL10N&MockObject $l;
|
||||
protected BreakoutRoomService $service;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->config = $this->createMock(Config::class);
|
||||
$this->manager = $this->createMock(Manager::class);
|
||||
$this->roomService = $this->createMock(RoomService::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->chatManager = $this->createMock(ChatManager::class);
|
||||
$this->notificationManager = $this->createMock(INotificationManager::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
$this->l = $this->createMock(IL10N::class);
|
||||
$this->service = new BreakoutRoomService(
|
||||
$this->config,
|
||||
$this->manager,
|
||||
$this->roomService,
|
||||
$this->participantService,
|
||||
$this->chatManager,
|
||||
$this->notificationManager,
|
||||
$this->timeFactory,
|
||||
$this->dispatcher,
|
||||
$this->l
|
||||
);
|
||||
}
|
||||
public static function dataParseAttendeeMap(): array {
|
||||
return [
|
||||
'Empty string means no map' => ['', 3, [], false],
|
||||
'Empty array means no map' => ['[]', 3, [], false],
|
||||
'OK' => [json_encode([1 => 1, 13 => 0, 42 => 2]), 3, [1 => 1, 13 => 0, 42 => 2], false],
|
||||
'Not an array' => ['"hello"', 3, null, true],
|
||||
'Room above max' => [json_encode([1 => 0, 13 => 1, 42 => 2]), 2, null, true],
|
||||
'Room below min' => [json_encode([1 => 0, 13 => -1, 42 => 2]), 3, null, true],
|
||||
'Room not int' => [json_encode([1 => 0, 13 => 'foo', 42 => 2]), 3, null, true],
|
||||
'Room null' => [json_encode([1 => 0, 13 => null, 42 => 2]), 3, null, true],
|
||||
'Attendee not int' => [json_encode([1 => 0, 'foo' => 1, 42 => 2]), 3, null, true],
|
||||
'Attendee negative' => [json_encode([1 => 0, -13 => 1, 42 => 2]), 3, null, true],
|
||||
'Attendee zero' => [json_encode([1 => 0, 0 => 1, 42 => 2]), 3, null, true],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataParseAttendeeMap')]
|
||||
public function testParseAttendeeMap(string $json, int $max, ?array $expected, bool $throws): void {
|
||||
if ($throws) {
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
}
|
||||
|
||||
$actual = self::invokePrivate($this->service, 'parseAttendeeMap', [$json, $max]);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Service;
|
||||
|
||||
use OCA\Talk\Service\CertificateService;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class CertificateServiceTest extends TestCase {
|
||||
protected CertificateService $service;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
$this->service = new CertificateService($logger);
|
||||
}
|
||||
|
||||
public function testGetParsedTlsHost(): void {
|
||||
$actual = $this->service->getParsedTlsHost('domain.com');
|
||||
$this->assertEquals($actual, 'domain.com');
|
||||
|
||||
$actual = $this->service->getParsedTlsHost('subdomain.domain.com');
|
||||
$this->assertEquals($actual, 'subdomain.domain.com');
|
||||
|
||||
$actual = $this->service->getParsedTlsHost('https://domain.com');
|
||||
$this->assertEquals($actual, 'domain.com');
|
||||
|
||||
$actual = $this->service->getParsedTlsHost('https://domain.com:1234');
|
||||
$this->assertEquals($actual, 'domain.com:1234');
|
||||
|
||||
$actual = $this->service->getParsedTlsHost('https://domain.com:1234/path/1/');
|
||||
$this->assertEquals($actual, 'domain.com:1234');
|
||||
|
||||
$actual = $this->service->getParsedTlsHost('http://domain.com:1234/path/1/');
|
||||
$this->assertNull($actual);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Service;
|
||||
|
||||
use OCA\Talk\Exceptions\UnauthorizedException;
|
||||
use OCA\Talk\Service\ChecksumVerificationService;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Test\TestCase;
|
||||
|
||||
class ChecksumVerificationServiceTest extends TestCase {
|
||||
protected ChecksumVerificationService $service;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->service = new ChecksumVerificationService();
|
||||
}
|
||||
|
||||
public static function dataValidateRequest(): array {
|
||||
$validRandom = md5(random_bytes(15));
|
||||
$fakeData = json_encode(['fake' => 'data']);
|
||||
$validSecret = 'valid secret';
|
||||
$validChecksum = hash_hmac('sha256', $validRandom . $fakeData, $validSecret);
|
||||
return [
|
||||
['', '', '', '', '', false],
|
||||
['1234', '', '', '', 'Invalid random provided', false],
|
||||
[str_repeat('1', 32), '', '', '', 'Invalid checksum provided', false],
|
||||
[str_repeat('1', 32), 'fake', '', '', 'No secret provided', false],
|
||||
[str_repeat('1', 32), 'fake', 'invalid', '', 'Invalid HMAC provided', false],
|
||||
[$validRandom, $validChecksum, $validSecret, $fakeData, '', true],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataValidateRequest')]
|
||||
public function testValidateRequest(string $random, string $checksum, string $secret, string $token, string $exceptionMessage, bool $expectedReturn): void {
|
||||
if ($exceptionMessage) {
|
||||
$this->expectException(UnauthorizedException::class);
|
||||
$this->expectExceptionMessage($exceptionMessage);
|
||||
}
|
||||
$actual = $this->service->validateRequest($random, $checksum, $secret, $token);
|
||||
if (!$exceptionMessage) {
|
||||
$this->assertEquals($expectedReturn, $actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Service;
|
||||
|
||||
use OCA\Talk\Service\EmojiService;
|
||||
use OCP\IEmojiHelper;
|
||||
use OCP\Server;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class EmojiServiceTest extends TestCase {
|
||||
protected ?EmojiService $service = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->service = new EmojiService(
|
||||
Server::get(IEmojiHelper::class),
|
||||
);
|
||||
}
|
||||
|
||||
public static function dataGetFirstCombinedEmoji(): array {
|
||||
return [
|
||||
['👋 Hello', '👋'],
|
||||
['Only leading emojis 🚀', ''],
|
||||
['👩🏽💻👩🏻💻👨🏿💻 Only one, but with all attributes', '👩🏽💻'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataGetFirstCombinedEmoji')]
|
||||
public function testGetFirstCombinedEmoji(string $roomName, string $avatarEmoji): void {
|
||||
$this->assertSame($avatarEmoji, self::invokePrivate($this->service, 'getFirstCombinedEmoji', [$roomName]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Service;
|
||||
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Federation\BackendNotifier;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Model\AttendeeMapper;
|
||||
use OCA\Talk\Model\Session;
|
||||
use OCA\Talk\Model\SessionMapper;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\MembershipService;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\SessionService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Federation\ICloudIdManager;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use OCP\UserStatus\IManager;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class ParticipantServiceTest extends TestCase {
|
||||
protected IConfig&MockObject $serverConfig;
|
||||
protected Config&MockObject $talkConfig;
|
||||
protected ?AttendeeMapper $attendeeMapper = null;
|
||||
protected ?SessionMapper $sessionMapper = null;
|
||||
protected SessionService&MockObject $sessionService;
|
||||
protected ISecureRandom&MockObject $secureRandom;
|
||||
protected IEventDispatcher&MockObject $dispatcher;
|
||||
protected IUserManager&MockObject $userManager;
|
||||
protected ICloudIdManager&MockObject $cloudIdManager;
|
||||
protected IGroupManager&MockObject $groupManager;
|
||||
protected MembershipService&MockObject $membershipService;
|
||||
protected BackendNotifier&MockObject $federationBackendNotifier;
|
||||
protected ITimeFactory&MockObject $time;
|
||||
protected ICacheFactory&MockObject $cacheFactory;
|
||||
protected IManager&MockObject $userStatusManager;
|
||||
private ?ParticipantService $service = null;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->serverConfig = $this->createMock(IConfig::class);
|
||||
$this->talkConfig = $this->createMock(Config::class);
|
||||
$this->attendeeMapper = new AttendeeMapper(\OCP\Server::get(IDBConnection::class));
|
||||
$this->sessionMapper = new SessionMapper(\OCP\Server::get(IDBConnection::class));
|
||||
$this->sessionService = $this->createMock(SessionService::class);
|
||||
$this->secureRandom = $this->createMock(ISecureRandom::class);
|
||||
$this->dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->cloudIdManager = $this->createMock(ICloudIdManager::class);
|
||||
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||
$this->membershipService = $this->createMock(MembershipService::class);
|
||||
$this->federationBackendNotifier = $this->createMock(BackendNotifier::class);
|
||||
$this->time = $this->createMock(ITimeFactory::class);
|
||||
$this->cacheFactory = $this->createMock(ICacheFactory::class);
|
||||
$this->userStatusManager = $this->createMock(IManager::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->service = new ParticipantService(
|
||||
$this->serverConfig,
|
||||
$this->talkConfig,
|
||||
$this->attendeeMapper,
|
||||
$this->sessionMapper,
|
||||
$this->sessionService,
|
||||
$this->secureRandom,
|
||||
\OCP\Server::get(IDBConnection::class),
|
||||
$this->dispatcher,
|
||||
$this->userManager,
|
||||
$this->cloudIdManager,
|
||||
$this->groupManager,
|
||||
$this->membershipService,
|
||||
$this->federationBackendNotifier,
|
||||
$this->time,
|
||||
$this->cacheFactory,
|
||||
$this->userStatusManager,
|
||||
$this->logger
|
||||
);
|
||||
}
|
||||
|
||||
public function tearDown(): void {
|
||||
try {
|
||||
$attendee = $this->attendeeMapper->findByActor(123456789, Attendee::ACTOR_USERS, 'test');
|
||||
$this->sessionMapper->deleteByAttendeeId($attendee->getId());
|
||||
$this->attendeeMapper->delete($attendee);
|
||||
} catch (DoesNotExistException $exception) {
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testGetParticipantsByNotificationLevel(): void {
|
||||
$attendee = new Attendee();
|
||||
$attendee->setActorType(Attendee::ACTOR_USERS);
|
||||
$attendee->setActorId('test');
|
||||
$attendee->setRoomId(123456789);
|
||||
$attendee->setNotificationLevel(Participant::NOTIFY_MENTION);
|
||||
$this->attendeeMapper->insert($attendee);
|
||||
|
||||
$session1 = new Session();
|
||||
$session1->setAttendeeId($attendee->getId());
|
||||
$session1->setSessionId(self::getUniqueID('session1'));
|
||||
$this->sessionMapper->insert($session1);
|
||||
|
||||
$session2 = new Session();
|
||||
$session2->setAttendeeId($attendee->getId());
|
||||
$session2->setSessionId(self::getUniqueID('session2'));
|
||||
$this->sessionMapper->insert($session2);
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getId')
|
||||
->willReturn(123456789);
|
||||
$participants = $this->service->getParticipantsByNotificationLevel($room, Participant::NOTIFY_MENTION);
|
||||
self::assertCount(1, $participants);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Service;
|
||||
|
||||
use OCA\Talk\Exceptions\InvalidRoomException;
|
||||
use OCA\Talk\Model\ProxyCacheMessage;
|
||||
use OCA\Talk\Model\ProxyCacheMessageMapper;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ProxyCacheMessageService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IDBConnection;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class ProxyCacheMessageServiceTest extends TestCase {
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected ?ProxyCacheMessageMapper $mapper = null;
|
||||
protected ?ProxyCacheMessageService $service = null;
|
||||
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->mapper = new ProxyCacheMessageMapper(\OCP\Server::get(IDBConnection::class));
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
|
||||
$this->service = new ProxyCacheMessageService(
|
||||
$this->mapper,
|
||||
$this->logger,
|
||||
$this->timeFactory,
|
||||
);
|
||||
$this->clearMessages();
|
||||
}
|
||||
|
||||
public function tearDown(): void {
|
||||
$this->clearMessages();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
protected function clearMessages(): void {
|
||||
$query = \OCP\Server::get(IDBConnection::class)->getQueryBuilder();
|
||||
$query->delete('talk_proxy_messages')
|
||||
->where($query->expr()->eq('remote_server_url', $query->createNamedParameter('phpunittests')));
|
||||
$query->executeStatement();
|
||||
}
|
||||
|
||||
public static function dataDeleteExpiredMessages(): array {
|
||||
return [
|
||||
[1234, 12345, true],
|
||||
[1234567, 12345, false],
|
||||
[null, 12345, false],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataDeleteExpiredMessages')]
|
||||
public function testDeleteExpiredMessages(?int $messageTime, int $currentTime, bool $expired): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('isFederatedConversation')
|
||||
->willReturn(true);
|
||||
|
||||
$m1 = new ProxyCacheMessage();
|
||||
$m1->setLocalToken('local_token');
|
||||
$m1->setRemoteServerUrl('phpunittests');
|
||||
$m1->setRemoteToken('remote_token');
|
||||
$m1->setRemoteMessageId(12345);
|
||||
$m1->setActorType('actor_type');
|
||||
$m1->setActorId('actor_id');
|
||||
$m1->setMessageType('message_type');
|
||||
if ($messageTime === null) {
|
||||
$m1->setExpirationDatetime($messageTime);
|
||||
} else {
|
||||
$m1->setExpirationDatetime(new \DateTime('@' . $messageTime));
|
||||
}
|
||||
$this->mapper->insert($m1);
|
||||
|
||||
$this->mapper->findById($room, $m1->getId());
|
||||
|
||||
$this->timeFactory->method('getDateTime')
|
||||
->willReturn(new \DateTime('@' . $currentTime));
|
||||
$this->service->deleteExpiredMessages();
|
||||
|
||||
if ($expired) {
|
||||
$this->expectException(DoesNotExistException::class);
|
||||
}
|
||||
$actual = $this->mapper->findById($room, $m1->getId());
|
||||
if (!$expired) {
|
||||
$this->assertEquals($m1->getId(), $actual->getId());
|
||||
}
|
||||
}
|
||||
|
||||
public function testFindByIdThrows(): void {
|
||||
$this->expectException(InvalidRoomException::class);
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('isFederatedConversation')
|
||||
->willReturn(false);
|
||||
|
||||
$this->mapper->findById($room, 42);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Service;
|
||||
|
||||
/**
|
||||
* Overwrite is_uploaded_file in the OCA\Talk\Service namespace
|
||||
* to allow proper unit testing of the postAvatar call.
|
||||
*/
|
||||
function is_uploaded_file($filename) {
|
||||
return file_exists($filename);
|
||||
}
|
||||
|
||||
namespace OCA\Talk\Tests\php\Service;
|
||||
|
||||
use OCA\Talk\Chat\ChatManager;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Recording\BackendNotifier;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\RecordingService;
|
||||
use OCA\Talk\Service\RoomService;
|
||||
use OCP\AppFramework\Services\IAppConfig;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Files\IMimeTypeDetector;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IConfig;
|
||||
use OCP\IUserManager;
|
||||
use OCP\L10N\IFactory;
|
||||
use OCP\Notification\IManager;
|
||||
use OCP\Share\IManager as ShareManager;
|
||||
use OCP\TaskProcessing\IManager as ITaskProcessingManager;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class RecordingServiceTest extends TestCase {
|
||||
private IMimeTypeDetector $mimeTypeDetector;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected IRootFolder&MockObject $rootFolder;
|
||||
protected Config&MockObject $config;
|
||||
protected IConfig&MockObject $serverConfig;
|
||||
protected IAppConfig&MockObject $appConfig;
|
||||
protected IManager&MockObject $notificationManager;
|
||||
protected Manager&MockObject $roomManager;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected RoomService&MockObject $roomService;
|
||||
protected ShareManager&MockObject $shareManager;
|
||||
protected ChatManager&MockObject $chatManager;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
protected BackendNotifier&MockObject $backendNotifier;
|
||||
protected ITaskProcessingManager&MockObject $taskProcessingManager;
|
||||
protected IFactory&MockObject $l10nFactory;
|
||||
protected IUserManager&MockObject $userManager;
|
||||
protected RecordingService $recordingService;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->mimeTypeDetector = \OCP\Server::get(IMimeTypeDetector::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->rootFolder = $this->createMock(IRootFolder::class);
|
||||
$this->notificationManager = $this->createMock(IManager::class);
|
||||
$this->roomManager = $this->createMock(Manager::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->config = $this->createMock(Config::class);
|
||||
$this->serverConfig = $this->createMock(IConfig::class);
|
||||
$this->appConfig = $this->createMock(IAppConfig::class);
|
||||
$this->roomService = $this->createMock(RoomService::class);
|
||||
$this->shareManager = $this->createMock(ShareManager::class);
|
||||
$this->chatManager = $this->createMock(ChatManager::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->backendNotifier = $this->createMock(BackendNotifier::class);
|
||||
$this->taskProcessingManager = $this->createMock(ITaskProcessingManager::class);
|
||||
$this->l10nFactory = $this->createMock(IFactory::class);
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
|
||||
$this->recordingService = new RecordingService(
|
||||
$this->mimeTypeDetector,
|
||||
$this->participantService,
|
||||
$this->rootFolder,
|
||||
$this->notificationManager,
|
||||
$this->roomManager,
|
||||
$this->timeFactory,
|
||||
$this->config,
|
||||
$this->serverConfig,
|
||||
$this->appConfig,
|
||||
$this->roomService,
|
||||
$this->shareManager,
|
||||
$this->chatManager,
|
||||
$this->logger,
|
||||
$this->backendNotifier,
|
||||
$this->taskProcessingManager,
|
||||
$this->l10nFactory,
|
||||
$this->userManager,
|
||||
);
|
||||
}
|
||||
|
||||
public static function dataValidateFileFormat(): array {
|
||||
return [
|
||||
# file_invalid_path
|
||||
['', '', 'file_invalid_path'],
|
||||
# file_mimetype
|
||||
['', realpath(__DIR__ . '/../../../img/app.svg'), 'file_mimetype'],
|
||||
['name.ogg', realpath(__DIR__ . '/../../../img/app.svg'), 'file_mimetype'],
|
||||
# file_extension
|
||||
['', realpath(__DIR__ . '/../../../img/join_call.ogg'), 'file_extension'],
|
||||
['name', realpath(__DIR__ . '/../../../img/join_call.ogg'), 'file_extension'],
|
||||
['name.mp3', realpath(__DIR__ . '/../../../img/join_call.ogg'), 'file_extension'],
|
||||
# Success
|
||||
['name.ogg', realpath(__DIR__ . '/../../../img/join_call.ogg'), ''],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataValidateFileFormat')]
|
||||
public function testValidateFileFormat(string $fileName, string $fileRealPath, string $exceptionMessage): void {
|
||||
if ($exceptionMessage) {
|
||||
$this->expectExceptionMessage($exceptionMessage);
|
||||
} else {
|
||||
$this->expectNotToPerformAssertions();
|
||||
}
|
||||
$this->recordingService->validateFileFormat($fileName, $fileRealPath);
|
||||
}
|
||||
|
||||
public static function dataGetResourceFromFileArray(): array {
|
||||
$fileWithContent = tempnam(sys_get_temp_dir(), 'txt');
|
||||
file_put_contents($fileWithContent, 'bla');
|
||||
return [
|
||||
[['error' => 1, 'tmp_name' => ''], '', 'invalid_file'],
|
||||
[['error' => 1, 'tmp_name' => 'a'], '', 'invalid_file'],
|
||||
# Empty file
|
||||
[['error' => 0, 'tmp_name' => tempnam(sys_get_temp_dir(), 'txt')], '', 'empty_file'],
|
||||
# file with content
|
||||
[['error' => 0, 'tmp_name' => $fileWithContent], 'bla', ''],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataGetResourceFromFileArray')]
|
||||
public function testGetResourceFromFileArray(array $file, string $expected, string $exceptionMessage): void {
|
||||
if ($exceptionMessage) {
|
||||
$this->expectExceptionMessage($exceptionMessage);
|
||||
}
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$attendee = Attendee::fromRow([
|
||||
'actor_type' => Attendee::ACTOR_USERS,
|
||||
'actor_id' => 'participant1',
|
||||
]);
|
||||
$participant = new Participant($room, $attendee, null);
|
||||
|
||||
$actual = stream_get_contents($this->recordingService->getResourceFromFileArray($file, $room, $participant));
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Service;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use OC\EventDispatcher\EventDispatcher;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Events\RoomPasswordVerifyEvent;
|
||||
use OCA\Talk\Exceptions\RoomNotFoundException;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Model\BreakoutRoom;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\EmojiService;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\RecordingService;
|
||||
use OCA\Talk\Service\RoomService;
|
||||
use OCA\Talk\Webinary;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\BackgroundJob\IJobList;
|
||||
use OCP\Calendar\IManager;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IL10N;
|
||||
use OCP\IUser;
|
||||
use OCP\Security\IHasher;
|
||||
use OCP\Server;
|
||||
use OCP\Share\IManager as IShareManager;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class RoomServiceTest extends TestCase {
|
||||
protected Manager&MockObject $manager;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected IShareManager&MockObject $shareManager;
|
||||
protected Config&MockObject $config;
|
||||
protected IHasher&MockObject $hasher;
|
||||
protected IEventDispatcher&MockObject $dispatcher;
|
||||
protected IJobList&MockObject $jobList;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
protected IL10N&MockObject $l10n;
|
||||
protected IManager $calendarManager;
|
||||
protected EmojiService $emojiService;
|
||||
protected ?RoomService $service = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->manager = $this->createMock(Manager::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->shareManager = $this->createMock(IShareManager::class);
|
||||
$this->config = $this->createMock(Config::class);
|
||||
$this->hasher = $this->createMock(IHasher::class);
|
||||
$this->dispatcher = $this->createMock(IEventDispatcher::class);
|
||||
$this->jobList = $this->createMock(IJobList::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->emojiService = Server::get(EmojiService::class);
|
||||
$this->calendarManager = $this->createMock(IManager::class);
|
||||
$this->service = new RoomService(
|
||||
$this->manager,
|
||||
$this->participantService,
|
||||
\OCP\Server::get(IDBConnection::class),
|
||||
$this->timeFactory,
|
||||
$this->shareManager,
|
||||
$this->config,
|
||||
$this->hasher,
|
||||
$this->dispatcher,
|
||||
$this->jobList,
|
||||
$this->emojiService,
|
||||
$this->logger,
|
||||
$this->l10n,
|
||||
$this->calendarManager,
|
||||
);
|
||||
}
|
||||
|
||||
public function testCreateOneToOneConversationWithSameUser(): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')
|
||||
->willReturn('uid');
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('invite');
|
||||
$this->service->createOneToOneConversation($user, $user);
|
||||
}
|
||||
|
||||
public function testCreateOneToOneConversationWithNotCurrentUserCanEnumerateTargetUser(): void {
|
||||
$user1 = $this->createMock(IUser::class);
|
||||
$user1->method('getUID')
|
||||
->willReturn('uid1');
|
||||
$user2 = $this->createMock(IUser::class);
|
||||
$user2->method('getUID')
|
||||
->willReturn('uid2');
|
||||
|
||||
$this->expectException(RoomNotFoundException::class);
|
||||
$this->shareManager
|
||||
->expects($this->once())
|
||||
->method('currentUserCanEnumerateTargetUser')
|
||||
->willReturn(false);
|
||||
$this->manager
|
||||
->method('getOne2OneRoom')
|
||||
->willThrowException(new RoomNotFoundException());
|
||||
$this->service->createOneToOneConversation($user1, $user2);
|
||||
}
|
||||
|
||||
public function testCreateOneToOneConversationAlreadyExists(): void {
|
||||
$user1 = $this->createMock(IUser::class);
|
||||
$user1->method('getUID')
|
||||
->willReturn('uid1');
|
||||
$user2 = $this->createMock(IUser::class);
|
||||
$user2->method('getUID')
|
||||
->willReturn('uid2');
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$this->participantService->expects($this->once())
|
||||
->method('ensureOneToOneRoomIsFilled')
|
||||
->with($room);
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('getOne2OneRoom')
|
||||
->with('uid1', 'uid2')
|
||||
->willReturn($room);
|
||||
|
||||
$this->assertSame($room, $this->service->createOneToOneConversation($user1, $user2));
|
||||
}
|
||||
|
||||
public function testCreateOneToOneConversationCreated(): void {
|
||||
$user1 = $this->createMock(IUser::class);
|
||||
$user1->method('getUID')
|
||||
->willReturn('uid1');
|
||||
$user1->method('getDisplayName')
|
||||
->willReturn('display-1');
|
||||
$user2 = $this->createMock(IUser::class);
|
||||
$user2->method('getUID')
|
||||
->willReturn('uid2');
|
||||
$user2->method('getDisplayName')
|
||||
->willReturn('display-2');
|
||||
|
||||
$this->shareManager
|
||||
->expects($this->once())
|
||||
->method('currentUserCanEnumerateTargetUser')
|
||||
->willReturn(true);
|
||||
|
||||
$room = $this->createMock(Room::class);
|
||||
$this->participantService->expects($this->once())
|
||||
->method('addUsers')
|
||||
->with($room, [[
|
||||
'actorType' => 'users',
|
||||
'actorId' => 'uid1',
|
||||
'displayName' => 'display-1',
|
||||
'participantType' => Participant::OWNER,
|
||||
]]);
|
||||
|
||||
$this->participantService->expects($this->never())
|
||||
->method('ensureOneToOneRoomIsFilled')
|
||||
->with($room);
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('getOne2OneRoom')
|
||||
->with('uid1', 'uid2')
|
||||
->willThrowException(new RoomNotFoundException());
|
||||
|
||||
$this->manager->expects($this->once())
|
||||
->method('createRoom')
|
||||
->with(Room::TYPE_ONE_TO_ONE)
|
||||
->willReturn($room);
|
||||
|
||||
$this->assertSame($room, $this->service->createOneToOneConversation($user1, $user2));
|
||||
}
|
||||
|
||||
public static function dataCreateConversationInvalidNames(): array {
|
||||
return [
|
||||
[''],
|
||||
[' '],
|
||||
[str_repeat('a', 256)],
|
||||
// Isn't a multibyte emoji
|
||||
[str_repeat('😃', 256)],
|
||||
// This is a multibyte emoji and need 2 chars in database
|
||||
// 256 / 2 = 128
|
||||
[str_repeat('💻', 128)],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataCreateConversationInvalidNames')]
|
||||
public function testCreateConversationInvalidNames(string $name): void {
|
||||
$this->manager->expects($this->never())
|
||||
->method('createRoom');
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('name');
|
||||
$this->service->createConversation(Room::TYPE_GROUP, $name);
|
||||
}
|
||||
|
||||
public static function dataCreateConversationInvalidTypes(): array {
|
||||
return [
|
||||
[Room::TYPE_ONE_TO_ONE],
|
||||
[Room::TYPE_UNKNOWN],
|
||||
[Room::TYPE_ONE_TO_ONE_FORMER],
|
||||
[7],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataCreateConversationInvalidTypes')]
|
||||
public function testCreateConversationInvalidTypes(int $type): void {
|
||||
$this->manager->expects($this->never())
|
||||
->method('createRoom');
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('type');
|
||||
$this->service->createConversation($type, 'abc');
|
||||
}
|
||||
|
||||
public static function dataCreateConversationInvalidObjects(): array {
|
||||
return [
|
||||
[str_repeat('a', 65), 'a', 'object-type'],
|
||||
['a', str_repeat('a', 65), 'object-id'],
|
||||
['a', '', 'object'],
|
||||
['', 'b', 'object'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataCreateConversationInvalidObjects')]
|
||||
public function testCreateConversationInvalidObjects(string $type, string $id, string $exception): void {
|
||||
$this->manager->expects($this->never())
|
||||
->method('createRoom');
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage($exception);
|
||||
$this->service->createConversation(Room::TYPE_PUBLIC, 'a', null, $type, $id);
|
||||
}
|
||||
|
||||
public static function dataCreateConversation(): array {
|
||||
return [
|
||||
[Room::TYPE_GROUP, 'Group conversation', 'admin', '', '', ''],
|
||||
[Room::TYPE_PUBLIC, 'Public conversation', '', 'file', '123456', ''],
|
||||
[Room::TYPE_PUBLIC, 'Public conversation', '', 'file', '123456', 'AGoodPassword123?'],
|
||||
[Room::TYPE_CHANGELOG, 'Talk updates ✅', 'test1', '', '', ''],
|
||||
[Room::TYPE_GROUP, 'Let\'s get started!', 'test1', 'sample', 'test1', ''],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataCreateConversation')]
|
||||
public function testCreateConversation(int $type, string $name, string $ownerId, string $objectType, string $objectId, string $password): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
|
||||
if ($ownerId !== '') {
|
||||
$owner = $this->createMock(IUser::class);
|
||||
$owner->method('getUID')
|
||||
->willReturn($ownerId);
|
||||
$owner->method('getDisplayName')
|
||||
->willReturn($ownerId . '-display');
|
||||
|
||||
$this->participantService->expects($this->once())
|
||||
->method('addUsers')
|
||||
->with($room, [[
|
||||
'actorType' => 'users',
|
||||
'actorId' => $ownerId,
|
||||
'displayName' => $ownerId . '-display',
|
||||
'participantType' => Participant::OWNER,
|
||||
]]);
|
||||
} else {
|
||||
$owner = null;
|
||||
$this->participantService->expects($this->never())
|
||||
->method('addUsers');
|
||||
}
|
||||
|
||||
if ($password !== '') {
|
||||
$this->hasher->expects(self::once())
|
||||
->method('hash')
|
||||
->willReturn($password);
|
||||
}
|
||||
$this->manager->expects($this->once())
|
||||
->method('createRoom')
|
||||
->with($type, $name, $objectType, $objectId, $password)
|
||||
->willReturn($room);
|
||||
|
||||
$this->assertSame($room, $this->service->createConversation($type, $name, $owner, $objectType, $objectId, $password));
|
||||
}
|
||||
|
||||
public static function dataPrepareConversationName(): array {
|
||||
return [
|
||||
['', ''],
|
||||
[' ', ''],
|
||||
['A ', 'A'],
|
||||
[' B', 'B'],
|
||||
[' C ', 'C'],
|
||||
['A' . str_repeat(' ', 100) . 'B', 'A'],
|
||||
['A' . str_repeat(' ', 32) . 'B', 'A' . str_repeat(' ', 32) . 'B'],
|
||||
['Лорем ипсум долор сит амет, но антиопам алияуандо витуперата еам, мел те цонгуе хомеро адолесценс.', 'Лорем ипсум долор сит амет, но антиопам алияуандо витуперата еам'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataPrepareConversationName')]
|
||||
public function testPrepareConversationName(string $input, string $expected): void {
|
||||
$this->assertSame($expected, $this->service->prepareConversationName($input));
|
||||
}
|
||||
|
||||
public function testVerifyPassword(): void {
|
||||
$dispatcher = new EventDispatcher(
|
||||
new \Symfony\Component\EventDispatcher\EventDispatcher(),
|
||||
\OC::$server,
|
||||
$this->createMock(LoggerInterface::class)
|
||||
);
|
||||
$dispatcher->addListener(RoomPasswordVerifyEvent::class, static function (RoomPasswordVerifyEvent $event): void {
|
||||
$password = $event->getPassword();
|
||||
|
||||
if ($password === '1234') {
|
||||
$event->setIsPasswordValid(true);
|
||||
$event->setRedirectUrl('');
|
||||
} else {
|
||||
$event->setIsPasswordValid(false);
|
||||
$event->setRedirectUrl('https://test');
|
||||
}
|
||||
});
|
||||
|
||||
$service = new RoomService(
|
||||
$this->manager,
|
||||
$this->participantService,
|
||||
\OCP\Server::get(IDBConnection::class),
|
||||
$this->timeFactory,
|
||||
$this->shareManager,
|
||||
$this->config,
|
||||
$this->hasher,
|
||||
$dispatcher,
|
||||
$this->jobList,
|
||||
$this->emojiService,
|
||||
$this->logger,
|
||||
$this->l10n,
|
||||
$this->calendarManager,
|
||||
);
|
||||
|
||||
$room = new Room(
|
||||
$this->createMock(Manager::class),
|
||||
$this->createMock(IDBConnection::class),
|
||||
$dispatcher,
|
||||
$this->createMock(ITimeFactory::class),
|
||||
1,
|
||||
Room::TYPE_PUBLIC,
|
||||
Room::READ_WRITE,
|
||||
Room::LISTABLE_NONE,
|
||||
0,
|
||||
Webinary::LOBBY_NONE,
|
||||
Webinary::SIP_DISABLED,
|
||||
null,
|
||||
'foobar',
|
||||
'Test',
|
||||
'description',
|
||||
'passy',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
Attendee::PERMISSIONS_DEFAULT,
|
||||
Attendee::PERMISSIONS_DEFAULT,
|
||||
Participant::FLAG_DISCONNECTED,
|
||||
null,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
'',
|
||||
'',
|
||||
BreakoutRoom::MODE_NOT_CONFIGURED,
|
||||
BreakoutRoom::STATUS_STOPPED,
|
||||
Room::RECORDING_NONE,
|
||||
RecordingService::CONSENT_REQUIRED_NO,
|
||||
Room::HAS_FEDERATION_NONE,
|
||||
Room::MENTION_PERMISSIONS_EVERYONE,
|
||||
'',
|
||||
);
|
||||
|
||||
$verificationResult = $service->verifyPassword($room, '1234');
|
||||
$this->assertSame($verificationResult, ['result' => true, 'url' => '']);
|
||||
$verificationResult = $service->verifyPassword($room, '4321');
|
||||
$this->assertSame($verificationResult, ['result' => false, 'url' => 'https://test']);
|
||||
$this->assertSame('passy', $room->getPassword());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Service;
|
||||
|
||||
use OCA\Talk\Service\SIPDialOutService;
|
||||
use OCA\Talk\Signaling\BackendNotifier;
|
||||
use OCA\Talk\Signaling\Responses\DialOut;
|
||||
use OCA\Talk\Signaling\Responses\DialOutError;
|
||||
use OCA\Talk\Signaling\Responses\Response;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class SIPDialOutServiceTest extends TestCase {
|
||||
protected BackendNotifier&MockObject $backendNotifier;
|
||||
protected LoggerInterface&MockObject $logger;
|
||||
protected ?SIPDialOutService $service = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->backendNotifier = $this->createMock(BackendNotifier::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->service = new SIPDialOutService(
|
||||
$this->backendNotifier,
|
||||
$this->logger,
|
||||
);
|
||||
}
|
||||
|
||||
public function testValidateDialOutResponseSuccess(): void {
|
||||
$data = <<<JSON
|
||||
{
|
||||
"type": "dialout",
|
||||
"dialout": {
|
||||
"callid": "the-call-id"
|
||||
}
|
||||
}
|
||||
JSON;
|
||||
|
||||
/** @var Response $response */
|
||||
$response = self::invokePrivate($this->service, 'validateDialOutResponse', [$data]);
|
||||
|
||||
$this->assertInstanceOf(Response::class, $response);
|
||||
$this->assertInstanceOf(DialOut::class, $response->dialOut);
|
||||
$this->assertSame('the-call-id', $response->dialOut->callId);
|
||||
$this->assertNull($response->dialOut->error);
|
||||
}
|
||||
|
||||
public function testValidateDialOutResponseError(): void {
|
||||
$data = <<<JSON
|
||||
{
|
||||
"type": "dialout",
|
||||
"dialout": {
|
||||
"error": {
|
||||
"code": "error-code",
|
||||
"message": "Human readable error."
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON;
|
||||
|
||||
/** @var Response $response */
|
||||
$response = self::invokePrivate($this->service, 'validateDialOutResponse', [$data]);
|
||||
|
||||
$this->assertInstanceOf(Response::class, $response);
|
||||
$this->assertInstanceOf(DialOut::class, $response->dialOut);
|
||||
$this->assertInstanceOf(DialOutError::class, $response->dialOut->error);
|
||||
$this->assertNull($response->dialOut->callId);
|
||||
$this->assertSame('error-code', $response->dialOut->error->code);
|
||||
$this->assertSame('Human readable error.', $response->dialOut->error->message);
|
||||
}
|
||||
|
||||
public function testValidateDialOutResponseErrorWithDetails(): void {
|
||||
$data = <<<JSON
|
||||
{
|
||||
"type": "dialout",
|
||||
"dialout": {
|
||||
"error": {
|
||||
"code": "error-code",
|
||||
"message": "Human readable error.",
|
||||
"details": {
|
||||
"attendeeId": 32
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON;
|
||||
|
||||
/** @var Response $response */
|
||||
$response = self::invokePrivate($this->service, 'validateDialOutResponse', [$data]);
|
||||
|
||||
$this->assertInstanceOf(Response::class, $response);
|
||||
$this->assertInstanceOf(DialOut::class, $response->dialOut);
|
||||
$this->assertInstanceOf(DialOutError::class, $response->dialOut->error);
|
||||
$this->assertNull($response->dialOut->callId);
|
||||
$this->assertSame('error-code', $response->dialOut->error->code);
|
||||
$this->assertSame('Human readable error.', $response->dialOut->error->message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Service;
|
||||
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Model\SessionMapper;
|
||||
use OCA\Talk\Service\SessionService;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class SessionServiceTest extends TestCase {
|
||||
protected ?SessionMapper $sessionMapper = null;
|
||||
protected ISecureRandom&MockObject $secureRandom;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
private ?SessionService $service = null;
|
||||
|
||||
private const RANDOM_254 = '123456789abcdef0123456789abcdef1123456789abcdef2123456789abcdef3123456789abcdef4123456789abcdef5123456789abcdef6123456789abcdef7123456789abcdef8123456789abcdef9123456789abcdefa123456789abcdefb123456789abcdefc123456789abcdefd123456789abcdefe123456789abcde';
|
||||
|
||||
private array $attendeeIds = [];
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->sessionMapper = \OCP\Server::get(SessionMapper::class);
|
||||
$this->secureRandom = $this->createMock(ISecureRandom::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->service = new SessionService(
|
||||
$this->sessionMapper,
|
||||
\OCP\Server::get(IDBConnection::class),
|
||||
$this->secureRandom,
|
||||
$this->timeFactory,
|
||||
);
|
||||
}
|
||||
|
||||
public function tearDown(): void {
|
||||
foreach ($this->attendeeIds as $attendeeId) {
|
||||
try {
|
||||
$this->sessionMapper->deleteByAttendeeId($attendeeId);
|
||||
} catch (DoesNotExistException $exception) {
|
||||
}
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testCreateSessionForAttendee() {
|
||||
$attendee = new Attendee();
|
||||
$attendee->setId(42);
|
||||
$attendee->setActorType(Attendee::ACTOR_USERS);
|
||||
$attendee->setActorId('test');
|
||||
$this->attendeeIds[] = $attendee->getId();
|
||||
|
||||
$random = self::RANDOM_254 . 'x';
|
||||
|
||||
$this->secureRandom->expects($this->once())
|
||||
->method('generate')
|
||||
->with(255)
|
||||
->willReturn($random);
|
||||
|
||||
$session = $this->service->createSessionForAttendee($attendee);
|
||||
|
||||
self::assertEquals($random, $session->getSessionId());
|
||||
}
|
||||
|
||||
public function testCreateSessionForAttendeeWithDuplicatedSessionId() {
|
||||
$attendee1 = new Attendee();
|
||||
$attendee1->setId(42);
|
||||
$attendee1->setActorType(Attendee::ACTOR_USERS);
|
||||
$attendee1->setActorId('test1');
|
||||
$this->attendeeIds[] = $attendee1->getId();
|
||||
|
||||
$attendee2 = new Attendee();
|
||||
$attendee2->setId(108);
|
||||
$attendee2->setActorType(Attendee::ACTOR_USERS);
|
||||
$attendee2->setActorId('test2');
|
||||
$this->attendeeIds[] = $attendee2->getId();
|
||||
|
||||
$random1 = self::RANDOM_254 . 'x';
|
||||
$random2 = self::RANDOM_254 . 'y';
|
||||
|
||||
$this->secureRandom->expects($this->exactly(3))
|
||||
->method('generate')
|
||||
->with(255)
|
||||
->willReturn(
|
||||
$random1,
|
||||
$random1,
|
||||
$random2,
|
||||
);
|
||||
|
||||
$session1 = $this->service->createSessionForAttendee($attendee1);
|
||||
$session2 = $this->service->createSessionForAttendee($attendee2);
|
||||
|
||||
self::assertEquals($random1, $session1->getSessionId());
|
||||
self::assertEquals($random2, $session2->getSessionId());
|
||||
}
|
||||
|
||||
public function testCreateSessionForAttendeeWithoutId() {
|
||||
$attendee = new Attendee();
|
||||
$attendee->setActorType(Attendee::ACTOR_USERS);
|
||||
$attendee->setActorId('test');
|
||||
|
||||
$random = self::RANDOM_254 . 'x';
|
||||
|
||||
$this->secureRandom->expects($this->once())
|
||||
->method('generate')
|
||||
->with(255)
|
||||
->willReturn($random);
|
||||
|
||||
$this->expectException(\OC\DB\Exceptions\DbalException::class);
|
||||
|
||||
$session = $this->service->createSessionForAttendee($attendee);
|
||||
}
|
||||
|
||||
public function testCreateSessionForAttendeeWithInvitedCloudId() {
|
||||
$attendee = new Attendee();
|
||||
$attendee->setId(42);
|
||||
$attendee->setActorType(Attendee::ACTOR_USERS);
|
||||
$attendee->setActorId('test');
|
||||
$this->attendeeIds[] = $attendee->getId();
|
||||
|
||||
$random = self::RANDOM_254 . 'x';
|
||||
|
||||
$this->secureRandom->expects($this->once())
|
||||
->method('generate')
|
||||
->with(255)
|
||||
->willReturn($random);
|
||||
|
||||
$cloudId = 'user@server.com';
|
||||
$attendee->setInvitedCloudId($cloudId);
|
||||
|
||||
$session = $this->service->createSessionForAttendee($attendee);
|
||||
|
||||
self::assertEquals($random . '#' . $cloudId, $session->getSessionId());
|
||||
}
|
||||
|
||||
public function testExtendSessionIdWithMaximumLengthCloudId(): void {
|
||||
$attendee = new Attendee();
|
||||
$attendee->setId(42);
|
||||
$attendee->setActorType(Attendee::ACTOR_USERS);
|
||||
$attendee->setActorId('test');
|
||||
$this->attendeeIds[] = $attendee->getId();
|
||||
|
||||
$random = self::RANDOM_254 . 'x';
|
||||
|
||||
$this->secureRandom->expects($this->once())
|
||||
->method('generate')
|
||||
->with(255)
|
||||
->willReturn($random);
|
||||
|
||||
// User ids are 64 characters long at most; total cloud id length needs
|
||||
// to leave room for the '#' joining the ids.
|
||||
$cloudId = 'user123456789abcdef0123456789abcdef1123456789abcdef2123456789abc@server123456789abcdef0123456789abcdef1123456789abcdef2123456789abcdef3123456789abcdef4123456789abcdef5123456789abcdef6123456789abcdef7123456789abcdef8123456789abcdef9123456789abcdefa12345.com';
|
||||
$attendee->setInvitedCloudId($cloudId);
|
||||
|
||||
$session = $this->service->createSessionForAttendee($attendee);
|
||||
|
||||
self::assertEquals(256, strlen($cloudId));
|
||||
self::assertEquals(512, strlen($session->getSessionId()));
|
||||
self::assertEquals($random . '#' . $cloudId, $session->getSessionId());
|
||||
}
|
||||
|
||||
public function testExtendSessionIdWithTooLongCloudId(): void {
|
||||
$attendee = new Attendee();
|
||||
$attendee->setId(42);
|
||||
$attendee->setActorType(Attendee::ACTOR_USERS);
|
||||
$attendee->setActorId('test');
|
||||
$this->attendeeIds[] = $attendee->getId();
|
||||
|
||||
$random = self::RANDOM_254 . 'x';
|
||||
|
||||
$this->secureRandom->expects($this->once())
|
||||
->method('generate')
|
||||
->with(255)
|
||||
->willReturn($random);
|
||||
|
||||
// User ids are 64 characters long at most; total cloud id length needs
|
||||
// to leave room for the '#' joining the ids.
|
||||
$cloudId = 'user123456789abcdef0123456789abcdef1123456789abcdef2123456789abc@server123456789abcdef0123456789abcdef1123456789abcdef2123456789abcdef3123456789abcdef4123456789abcdef5123456789abcdef6123456789abcdef7123456789abcdef8123456789abcdef9123456789abcdefa123456.com';
|
||||
$trimmedCloudId = 'user123456789abcdef0123456789abcdef1123456789abcdef2123456789abc@server123456789abcdef0123456789abcdef1123456789abcdef2123456789abcdef3123456789abcdef4123456789abcdef5123456789abcdef6123456789abcdef7123456789abcdef8123456789abcdef9123456789abcdefa123456.co';
|
||||
$attendee->setInvitedCloudId($cloudId);
|
||||
|
||||
$session = $this->service->createSessionForAttendee($attendee);
|
||||
|
||||
self::assertEquals(257, strlen($cloudId));
|
||||
self::assertEquals(512, strlen($session->getSessionId()));
|
||||
self::assertEquals($random . '#' . $trimmedCloudId, $session->getSessionId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Settings\Admin;
|
||||
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\MatterbridgeManager;
|
||||
use OCA\Talk\Settings\Admin\AdminSettings;
|
||||
use OCP\AppFramework\Services\IAppConfig;
|
||||
use OCP\AppFramework\Services\IInitialState;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\IUserSession;
|
||||
use OCP\L10N\IFactory;
|
||||
use OCP\Support\Subscription\IRegistry;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class AdminSettingsTest extends TestCase {
|
||||
protected Config&MockObject $talkConfig;
|
||||
protected IConfig&MockObject $serverConfig;
|
||||
protected IAppConfig&MockObject $appConfig;
|
||||
protected IInitialState&MockObject $initialState;
|
||||
protected ICacheFactory&MockObject $cacheFactory;
|
||||
protected IGroupManager&MockObject $groupManager;
|
||||
protected MatterbridgeManager&MockObject $matterbridgeManager;
|
||||
protected IRegistry&MockObject $subscription;
|
||||
protected IUserSession&MockObject $userSession;
|
||||
protected IL10N&MockObject $l10n;
|
||||
protected IFactory&MockObject $l10nFactory;
|
||||
protected ?AdminSettings $admin = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->talkConfig = $this->createMock(Config::class);
|
||||
$this->serverConfig = $this->createMock(IConfig::class);
|
||||
$this->appConfig = $this->createMock(IAppConfig::class);
|
||||
$this->initialState = $this->createMock(IInitialState::class);
|
||||
$this->cacheFactory = $this->createMock(ICacheFactory::class);
|
||||
$this->groupManager = $this->createMock(IGroupManager::class);
|
||||
$this->matterbridgeManager = $this->createMock(MatterbridgeManager::class);
|
||||
$this->subscription = $this->createMock(IRegistry::class);
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->l10nFactory = $this->createMock(IFactory::class);
|
||||
|
||||
$this->admin = $this->getAdminSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $methods
|
||||
* @return AdminSettings|MockObject
|
||||
*/
|
||||
protected function getAdminSettings(array $methods = []): AdminSettings {
|
||||
if (empty($methods)) {
|
||||
return new AdminSettings(
|
||||
$this->talkConfig,
|
||||
$this->serverConfig,
|
||||
$this->appConfig,
|
||||
$this->initialState,
|
||||
$this->cacheFactory,
|
||||
$this->groupManager,
|
||||
$this->matterbridgeManager,
|
||||
$this->subscription,
|
||||
$this->userSession,
|
||||
$this->l10n,
|
||||
$this->l10nFactory
|
||||
);
|
||||
}
|
||||
|
||||
return $this->getMockBuilder(AdminSettings::class)
|
||||
->setConstructorArgs([
|
||||
$this->talkConfig,
|
||||
$this->serverConfig,
|
||||
$this->appConfig,
|
||||
$this->initialState,
|
||||
$this->cacheFactory,
|
||||
$this->groupManager,
|
||||
$this->matterbridgeManager,
|
||||
$this->subscription,
|
||||
$this->userSession,
|
||||
$this->l10n,
|
||||
$this->l10nFactory,
|
||||
])
|
||||
->onlyMethods($methods)
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function testGetSection(): void {
|
||||
$admin = $this->getAdminSettings();
|
||||
$this->assertNotEmpty($admin->getSection());
|
||||
}
|
||||
|
||||
public function testGetPriority(): void {
|
||||
$admin = $this->getAdminSettings();
|
||||
$this->assertEquals(0, $admin->getPriority());
|
||||
}
|
||||
|
||||
public function testGetForm(): void {
|
||||
$admin = $this->getAdminSettings([
|
||||
'initGeneralSettings',
|
||||
'initAllowedGroups',
|
||||
'initStunServers',
|
||||
'initTurnServers',
|
||||
'initSignalingServers',
|
||||
'initRequestSignalingServerTrial',
|
||||
]);
|
||||
|
||||
$admin->expects($this->once())
|
||||
->method('initGeneralSettings');
|
||||
$admin->expects($this->once())
|
||||
->method('initAllowedGroups');
|
||||
$admin->expects($this->once())
|
||||
->method('initStunServers');
|
||||
$admin->expects($this->once())
|
||||
->method('initTurnServers');
|
||||
$admin->expects($this->once())
|
||||
->method('initSignalingServers');
|
||||
$admin->expects($this->once())
|
||||
->method('initRequestSignalingServerTrial');
|
||||
|
||||
$form = $admin->getForm();
|
||||
$this->assertSame('settings/admin-settings', $form->getTemplateName());
|
||||
$this->assertSame('', $form->getRenderAs());
|
||||
$this->assertCount(0, $form->getParams());
|
||||
}
|
||||
|
||||
public function testInitStunServers(): void {
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('getStunServers')
|
||||
->willReturn(['getStunServers']);
|
||||
$this->serverConfig->expects($this->once())
|
||||
->method('getSystemValueBool')
|
||||
->with('has_internet_connection', true)
|
||||
->willReturn(true);
|
||||
|
||||
$i = 0;
|
||||
$expectedCalls = [
|
||||
['stun_servers', ['getStunServers']],
|
||||
['has_internet_connection', true],
|
||||
];
|
||||
$this->initialState->expects($this->exactly(2))
|
||||
->method('provideInitialState')
|
||||
->willReturnCallback(function () use ($expectedCalls, &$i): void {
|
||||
$this->assertArrayHasKey($i, $expectedCalls);
|
||||
$this->assertSame($expectedCalls[$i], func_get_args());
|
||||
$i++;
|
||||
});
|
||||
|
||||
$admin = $this->getAdminSettings();
|
||||
self::invokePrivate($admin, 'initStunServers');
|
||||
}
|
||||
|
||||
public function testInitTurnServers(): void {
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('getTurnServers')
|
||||
->willReturn(['getTurnServers']);
|
||||
|
||||
$this->initialState->expects($this->once())
|
||||
->method('provideInitialState')
|
||||
->with('turn_servers', ['getTurnServers']);
|
||||
|
||||
$admin = $this->getAdminSettings();
|
||||
self::invokePrivate($admin, 'initTurnServers');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Settings\Admin;
|
||||
|
||||
use OCA\Talk\Settings\Admin\Section;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class SectionTest extends TestCase {
|
||||
protected IURLGenerator&MockObject $url;
|
||||
protected IL10N&MockObject $l;
|
||||
protected ?Section $admin = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->url = $this->createMock(IURLGenerator::class);
|
||||
$this->l = $this->createMock(IL10N::class);
|
||||
|
||||
$this->admin = new Section($this->url, $this->l);
|
||||
}
|
||||
|
||||
public function testGetID(): void {
|
||||
$this->assertNotEmpty($this->admin->getID());
|
||||
}
|
||||
|
||||
public function testGetName(): void {
|
||||
$this->l->expects($this->once())
|
||||
->method('t')
|
||||
->with('Talk')
|
||||
->willReturnArgument(0);
|
||||
$this->assertNotEmpty($this->admin->getName());
|
||||
}
|
||||
|
||||
public function testGetIcon(): void {
|
||||
$this->url->expects($this->once())
|
||||
->method('imagePath')
|
||||
->with('spreed', 'app-dark.svg')
|
||||
->willReturn('apps/spreed/img/app-dark.svg');
|
||||
$this->assertNotEmpty($this->admin->getIcon());
|
||||
}
|
||||
|
||||
public function testGetPriority(): void {
|
||||
$this->assertGreaterThan(0, $this->admin->getPriority());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php\Signaling;
|
||||
|
||||
use OCA\Talk\Chat\ChatManager;
|
||||
use OCA\Talk\Chat\MessageParser;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Events\ARoomModifiedEvent;
|
||||
use OCA\Talk\Events\BeforeRoomDeletedEvent;
|
||||
use OCA\Talk\Events\ChatMessageSentEvent;
|
||||
use OCA\Talk\Events\GuestsCleanedUpEvent;
|
||||
use OCA\Talk\Events\LobbyModifiedEvent;
|
||||
use OCA\Talk\Events\RoomModifiedEvent;
|
||||
use OCA\Talk\Events\SystemMessageSentEvent;
|
||||
use OCA\Talk\Events\SystemMessagesMultipleSentEvent;
|
||||
use OCA\Talk\Manager;
|
||||
use OCA\Talk\Model\Message;
|
||||
use OCA\Talk\Model\Thread;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\SessionService;
|
||||
use OCA\Talk\Service\ThreadService;
|
||||
use OCA\Talk\Signaling\BackendNotifier;
|
||||
use OCA\Talk\Signaling\Listener;
|
||||
use OCA\Talk\Signaling\Messages;
|
||||
use OCA\Talk\Webinary;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Comments\IComment;
|
||||
use OCP\IL10N;
|
||||
use OCP\L10N\IFactory;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
#[Group('DB')]
|
||||
class ListenerTest extends TestCase {
|
||||
protected BackendNotifier&MockObject $backendNotifier;
|
||||
protected Manager&MockObject $manager;
|
||||
protected ParticipantService&MockObject $participantService;
|
||||
protected SessionService&MockObject $sessionService;
|
||||
protected ITimeFactory&MockObject $timeFactory;
|
||||
protected ?Listener $listener;
|
||||
protected MessageParser&MockObject $messageParser;
|
||||
protected ThreadService&MockObject $threadService;
|
||||
protected Config&MockObject $config;
|
||||
protected IFactory $l10nFactory;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->backendNotifier = $this->createMock(BackendNotifier::class);
|
||||
$this->manager = $this->createMock(Manager::class);
|
||||
$this->participantService = $this->createMock(ParticipantService::class);
|
||||
$this->sessionService = $this->createMock(SessionService::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->messageParser = $this->createMock(MessageParser::class);
|
||||
$this->threadService = $this->createMock(ThreadService::class);
|
||||
$this->l10nFactory = $this->createMock(IFactory::class);
|
||||
$this->config = $this->createMock(Config::class);
|
||||
|
||||
$this->listener = new Listener(
|
||||
$this->config,
|
||||
$this->createMock(Messages::class),
|
||||
$this->backendNotifier,
|
||||
$this->manager,
|
||||
$this->participantService,
|
||||
$this->sessionService,
|
||||
$this->timeFactory,
|
||||
$this->messageParser,
|
||||
$this->threadService,
|
||||
$this->l10nFactory,
|
||||
);
|
||||
}
|
||||
|
||||
public static function dataRoomModified(): array {
|
||||
return [
|
||||
[
|
||||
ARoomModifiedEvent::PROPERTY_NAME,
|
||||
'Test room',
|
||||
'name',
|
||||
],
|
||||
[
|
||||
ARoomModifiedEvent::PROPERTY_DESCRIPTION,
|
||||
'The description',
|
||||
'',
|
||||
],
|
||||
[
|
||||
ARoomModifiedEvent::PROPERTY_PASSWORD,
|
||||
'password',
|
||||
null,
|
||||
],
|
||||
[
|
||||
ARoomModifiedEvent::PROPERTY_TYPE,
|
||||
Room::TYPE_PUBLIC,
|
||||
Room::TYPE_GROUP,
|
||||
],
|
||||
[
|
||||
ARoomModifiedEvent::PROPERTY_READ_ONLY,
|
||||
Room::READ_ONLY,
|
||||
Room::READ_WRITE,
|
||||
],
|
||||
[
|
||||
ARoomModifiedEvent::PROPERTY_LISTABLE,
|
||||
Room::LISTABLE_ALL,
|
||||
Room::LISTABLE_NONE,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataRoomModified')]
|
||||
public function testRoomModified(string $property, mixed $newValue, mixed $oldValue): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
|
||||
$event = new RoomModifiedEvent(
|
||||
$room,
|
||||
$property,
|
||||
$newValue,
|
||||
$oldValue,
|
||||
null,
|
||||
);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('roomModified')
|
||||
->with($room);
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testRecordingStatusChanged(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getCallRecording')
|
||||
->willReturn(Room::RECORDING_VIDEO);
|
||||
|
||||
$event = new RoomModifiedEvent(
|
||||
$room,
|
||||
ARoomModifiedEvent::PROPERTY_CALL_RECORDING,
|
||||
Room::RECORDING_VIDEO,
|
||||
Room::RECORDING_NONE,
|
||||
null,
|
||||
);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('sendRoomMessage')
|
||||
->with($room, [
|
||||
'type' => 'recording',
|
||||
'recording' => [
|
||||
'status' => Room::RECORDING_VIDEO,
|
||||
],
|
||||
]);
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public static function dataRoomLobbyModified(): array {
|
||||
return [
|
||||
[
|
||||
Webinary::LOBBY_NON_MODERATORS,
|
||||
Webinary::LOBBY_NONE,
|
||||
null,
|
||||
true,
|
||||
],
|
||||
[
|
||||
Webinary::LOBBY_NONE,
|
||||
Webinary::LOBBY_NON_MODERATORS,
|
||||
null,
|
||||
false,
|
||||
],
|
||||
[
|
||||
Webinary::LOBBY_NONE,
|
||||
Webinary::LOBBY_NON_MODERATORS,
|
||||
new \DateTime(),
|
||||
false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataRoomLobbyModified')]
|
||||
public function testRoomLobbyModified(int $newValue, int $oldValue, ?\DateTime $lobbyTimer, bool $timerReached): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
|
||||
$event = new LobbyModifiedEvent(
|
||||
$room,
|
||||
$newValue,
|
||||
$oldValue,
|
||||
$lobbyTimer,
|
||||
$timerReached,
|
||||
);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('roomModified')
|
||||
->with($room);
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testRoomLobbyRemoved(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
|
||||
$event = new LobbyModifiedEvent(
|
||||
$room,
|
||||
Webinary::LOBBY_NONE,
|
||||
Webinary::LOBBY_NON_MODERATORS,
|
||||
null,
|
||||
true,
|
||||
);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('roomModified')
|
||||
->with($room);
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testRoomDelete(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
|
||||
$event = new BeforeRoomDeletedEvent(
|
||||
$room
|
||||
);
|
||||
|
||||
$this->participantService->method('getParticipantUserIds')
|
||||
->with($room)
|
||||
->willReturn(['user1', 'user2']);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('roomDeleted')
|
||||
->with($room, ['user1', 'user2']);
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testGuestsCleanedUpEvent(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
|
||||
$event = new GuestsCleanedUpEvent(
|
||||
$room
|
||||
);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('participantsModified')
|
||||
->with($room, []);
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testChatMessageInvisibleSentEvent(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getId')->willReturn(1);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getTopmostParentId')->willReturn(null);
|
||||
$comment->method('getVerb')->willReturn(ChatManager::VERB_MESSAGE);
|
||||
$comment->method('getId')->willReturn(1);
|
||||
|
||||
$event = new ChatMessageSentEvent(
|
||||
$room,
|
||||
$comment,
|
||||
);
|
||||
|
||||
$this->config->expects($this->once())
|
||||
->method('isChatRelayEnabled')
|
||||
->willReturn(true);
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('createMessage');
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('parseMessage');
|
||||
$this->l10nFactory->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn($this->createMock(IL10N::class));
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('sendRoomMessage')
|
||||
->with($room, [
|
||||
'type' => 'chat',
|
||||
'chat' => [
|
||||
'refresh' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testChatMessageSentEvent(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getId')->willReturn(1);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getTopmostParentId')->willReturn(null);
|
||||
$comment->method('getVerb')->willReturn(ChatManager::VERB_MESSAGE);
|
||||
$comment->method('getId')->willReturn(1);
|
||||
$message = $this->createConfiguredMock(Message::class, [
|
||||
'getVisibility' => true,
|
||||
'toArray' => [],
|
||||
'getMessageId' => 123,
|
||||
]);
|
||||
$l10n = $this->createMock(IL10N::class);
|
||||
|
||||
$event = new ChatMessageSentEvent(
|
||||
$room,
|
||||
$comment,
|
||||
);
|
||||
|
||||
$this->config->expects($this->once())
|
||||
->method('isChatRelayEnabled')
|
||||
->willReturn(true);
|
||||
$this->l10nFactory->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn($l10n);
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('createMessage')
|
||||
->with($room, null, $comment, $l10n)
|
||||
->willReturn($message);
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('parseMessage')
|
||||
->with($message);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('sendRoomMessage')
|
||||
->with($room, [
|
||||
'type' => 'chat',
|
||||
'chat' => [
|
||||
'refresh' => true,
|
||||
'comment' => [],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testChatMessageSentWithThreadEvent(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$room->method('getId')->willReturn(1);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getTopmostParentId')->willReturn(null);
|
||||
$comment->method('getVerb')->willReturn(ChatManager::VERB_MESSAGE);
|
||||
$comment->method('getId')->willReturn(1);
|
||||
$message = $this->createConfiguredMock(Message::class, [
|
||||
'getVisibility' => true,
|
||||
'toArray' => [],
|
||||
'getMessageId' => 123,
|
||||
]);
|
||||
|
||||
$l10n = $this->createMock(IL10N::class);
|
||||
$thread = $this->createMock(Thread::class);
|
||||
|
||||
$event = new ChatMessageSentEvent(
|
||||
$room,
|
||||
$comment,
|
||||
);
|
||||
|
||||
$this->config->expects($this->once())
|
||||
->method('isChatRelayEnabled')
|
||||
->willReturn(true);
|
||||
$this->l10nFactory->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn($l10n);
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('createMessage')
|
||||
->with($room, null, $comment, $l10n)
|
||||
->willReturn($message);
|
||||
$this->messageParser->expects($this->once())
|
||||
->method('parseMessage')
|
||||
->with($message);
|
||||
$this->threadService->expects($this->once())
|
||||
->method('findByThreadId')
|
||||
->willReturn($thread);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('sendRoomMessage')
|
||||
->with($room, [
|
||||
'type' => 'chat',
|
||||
'chat' => [
|
||||
'refresh' => true,
|
||||
'comment' => [],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testSystemMessageSentEvent(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getVerb')->willReturn(ChatManager::VERB_SYSTEM);
|
||||
|
||||
$event = new SystemMessageSentEvent(
|
||||
$room,
|
||||
$comment,
|
||||
skipLastActivityUpdate: false
|
||||
);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('sendRoomMessage')
|
||||
->with($room, [
|
||||
'type' => 'chat',
|
||||
'chat' => [
|
||||
'refresh' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testSystemMessageSentEventSkippingUpdate(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getMessage')->willReturn(json_encode(['message' => 'test']));
|
||||
|
||||
$event = new SystemMessageSentEvent(
|
||||
$room,
|
||||
$comment,
|
||||
skipLastActivityUpdate: true
|
||||
);
|
||||
|
||||
$this->backendNotifier->expects($this->never())
|
||||
->method('sendRoomMessage');
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
|
||||
public function testSystemMessagesMultipleSentEvent(): void {
|
||||
$room = $this->createMock(Room::class);
|
||||
$comment = $this->createMock(IComment::class);
|
||||
$comment->method('getVerb')->willReturn(ChatManager::VERB_SYSTEM);
|
||||
|
||||
$event = new SystemMessagesMultipleSentEvent(
|
||||
$room,
|
||||
$comment,
|
||||
);
|
||||
|
||||
$this->backendNotifier->expects($this->once())
|
||||
->method('sendRoomMessage')
|
||||
->with($room, [
|
||||
'type' => 'chat',
|
||||
'chat' => [
|
||||
'refresh' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->listener->handle($event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Tests\php;
|
||||
|
||||
use OCA\Talk\TalkSession;
|
||||
use OCP\ISession;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class TalkSessionTest extends TestCase {
|
||||
protected ISession&MockObject $session;
|
||||
protected ?TalkSession $talkSession = null;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->session = $this->createMock(ISession::class);
|
||||
$this->talkSession = new TalkSession($this->session);
|
||||
}
|
||||
|
||||
public static function dataGet(): array {
|
||||
return [
|
||||
'session is null' => [null, null],
|
||||
'corrupted json' => ['{invalid json', null],
|
||||
'no data for token' => [json_encode(['t2' => 'd2']), null],
|
||||
'valid case' => [json_encode(['t1' => 'd1']), 'd1'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataGet')]
|
||||
public function testGetSessionForRoom(?string $sessionData, ?string $expected): void {
|
||||
$this->session->expects($this->once())
|
||||
->method('get')
|
||||
->with('spreed-session')
|
||||
->willReturn($sessionData);
|
||||
$this->assertSame($expected, $this->talkSession->getSessionForRoom('t1'));
|
||||
}
|
||||
|
||||
#[DataProvider('dataGet')]
|
||||
public function testGetPasswordForRoom(?string $sessionData, ?string $expected): void {
|
||||
$this->session->expects($this->once())
|
||||
->method('get')
|
||||
->with('spreed-password')
|
||||
->willReturn($sessionData);
|
||||
$this->assertSame($expected, $this->talkSession->getPasswordForRoom('t1'));
|
||||
}
|
||||
|
||||
public static function dataSet(): array {
|
||||
return [
|
||||
'session is null' => [null, json_encode(['t1' => 'd1'])],
|
||||
'corrupted json' => ['{invalid json', json_encode(['t1' => 'd1'])],
|
||||
'no data for token' => [json_encode(['t2' => 'd2']), json_encode(['t2' => 'd2', 't1' => 'd1'])],
|
||||
'update data' => [json_encode(['t1' => 'd2']), json_encode(['t1' => 'd1'])],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataSet')]
|
||||
public function testSetSessionForRoom(?string $sessionData, ?string $expected): void {
|
||||
$this->session->expects($this->once())
|
||||
->method('get')
|
||||
->with('spreed-session')
|
||||
->willReturn($sessionData);
|
||||
$this->session->expects($this->once())
|
||||
->method('set')
|
||||
->with('spreed-session', $expected);
|
||||
$this->talkSession->setSessionForRoom('t1', 'd1');
|
||||
}
|
||||
|
||||
#[DataProvider('dataSet')]
|
||||
public function testSetPasswordForRoom(?string $sessionData, ?string $expected): void {
|
||||
$this->session->expects($this->once())
|
||||
->method('get')
|
||||
->with('spreed-password')
|
||||
->willReturn($sessionData);
|
||||
$this->session->expects($this->once())
|
||||
->method('set')
|
||||
->with('spreed-password', $expected);
|
||||
$this->talkSession->setPasswordForRoom('t1', 'd1');
|
||||
}
|
||||
|
||||
public static function dataRemove(): array {
|
||||
return [
|
||||
'session is null' => [null, json_encode([])],
|
||||
'corrupted json' => ['{invalid json', json_encode([])],
|
||||
'no data for token' => [json_encode(['t2' => 'd2']), json_encode(['t2' => 'd2'])],
|
||||
'remove data' => [json_encode(['t2' => 'd2', 't1' => 'd1']), json_encode(['t2' => 'd2'])],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('dataRemove')]
|
||||
public function testRemoveSessionForRoom(?string $sessionData, ?string $expected): void {
|
||||
$this->session->expects($this->once())
|
||||
->method('get')
|
||||
->with('spreed-session')
|
||||
->willReturn($sessionData);
|
||||
$this->session->expects($this->once())
|
||||
->method('set')
|
||||
->with('spreed-session', $expected);
|
||||
$this->talkSession->removeSessionForRoom('t1');
|
||||
}
|
||||
|
||||
#[DataProvider('dataRemove')]
|
||||
public function testRemovePasswordForRoom(?string $sessionData, ?string $expected): void {
|
||||
$this->session->expects($this->once())
|
||||
->method('get')
|
||||
->with('spreed-password')
|
||||
->willReturn($sessionData);
|
||||
$this->session->expects($this->once())
|
||||
->method('set')
|
||||
->with('spreed-password', $expected);
|
||||
$this->talkSession->removePasswordForRoom('t1');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\Server;
|
||||
|
||||
if (!defined('PHPUNIT_RUN')) {
|
||||
define('PHPUNIT_RUN', 1);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../../../../lib/base.php';
|
||||
require_once __DIR__ . '/../../../../tests/autoload.php';
|
||||
|
||||
Server::get(IAppManager::class)->loadApp('spreed');
|
||||
@@ -0,0 +1,2 @@
|
||||
"name","email"
|
||||
"Name","valid@example.tld"
|
||||
|
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
@@ -0,0 +1,5 @@
|
||||
"email","name"
|
||||
"valid-1@example.tld","Valid 1"
|
||||
"valid-2@example.tld","valid-2@example.tld"
|
||||
"invalid","Valid 2"
|
||||
"valid-1@example.tld","Valid 1 again"
|
||||
|
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
@@ -0,0 +1,2 @@
|
||||
"email"
|
||||
"valid@example.tld"
|
||||
|
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
bootstrap="bootstrap.php"
|
||||
timeoutForSmallTests="900"
|
||||
timeoutForMediumTests="900"
|
||||
timeoutForLargeTests="900"
|
||||
failOnDeprecation="true"
|
||||
failOnIncomplete="true"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true"
|
||||
failOnEmptyTestSuite="true"
|
||||
failOnNotice="true"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
|
||||
cacheDirectory=".phpunit.result.cache">
|
||||
<testsuite name="Talk App Tests">
|
||||
<directory>.</directory>
|
||||
</testsuite>
|
||||
<logging>
|
||||
</logging>
|
||||
<source>
|
||||
<include>
|
||||
<directory>../../../spreed/appinfo</directory>
|
||||
<directory>../../../spreed/lib</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
Reference in New Issue
Block a user