You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
481 lines
15 KiB
481 lines
15 KiB
<?php
|
|
|
|
/**
|
|
*
|
|
* @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com)
|
|
*
|
|
* @license GNU AGPL version 3 or any later version
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
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\Manager;
|
|
use OCA\Talk\Model\Attendee;
|
|
use OCA\Talk\Model\Session;
|
|
use OCA\Talk\Participant;
|
|
use OCA\Talk\Room;
|
|
use OCA\Talk\Service\ParticipantService;
|
|
use OCP\AppFramework\Utility\ITimeFactory;
|
|
use OCP\Comments\IComment;
|
|
use OCP\IConfig;
|
|
use OCP\Notification\IManager as INotificationManager;
|
|
use OCP\Notification\INotification;
|
|
use OCP\IUserManager;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use Test\TestCase;
|
|
|
|
class NotifierTest extends TestCase {
|
|
/** @var INotificationManager|MockObject */
|
|
protected $notificationManager;
|
|
/** @var IUserManager|MockObject */
|
|
protected $userManager;
|
|
/** @var ParticipantService|MockObject */
|
|
protected $participantService;
|
|
/** @var Manager|MockObject */
|
|
protected $manager;
|
|
/** @var IConfig|MockObject */
|
|
protected $config;
|
|
/** @var ITimeFactory|MockObject */
|
|
protected $timeFactory;
|
|
/** @var Util|MockObject */
|
|
protected $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->participantService = $this->createMock(ParticipantService::class);
|
|
$this->manager = $this->createMock(Manager::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->participantService,
|
|
$this->manager,
|
|
$this->config,
|
|
$this->timeFactory,
|
|
$this->util,
|
|
])
|
|
->onlyMethods($methods)
|
|
->getMock();
|
|
}
|
|
return new Notifier(
|
|
$this->notificationManager,
|
|
$this->userManager,
|
|
$this->participantService,
|
|
$this->manager,
|
|
$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);
|
|
|
|
$room->expects($this->any())
|
|
->method('getParticipant')
|
|
->willReturnCallback(function (string $actorId) use ($room, $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);
|
|
});
|
|
|
|
$this->manager->expects($this->any())
|
|
->method('getRoomById')
|
|
->willReturn($room);
|
|
return $room;
|
|
}
|
|
|
|
/**
|
|
* @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([]);
|
|
$alreadyNotifiedUsers = array_map(function ($user): array {
|
|
return [
|
|
'id' => $user,
|
|
'type' => Attendee::ACTOR_USERS,
|
|
];
|
|
}, $alreadyNotifiedUsers);
|
|
$expectedReturn = array_map(function ($user): array {
|
|
return [
|
|
'id' => $user,
|
|
'type' => Attendee::ACTOR_USERS,
|
|
];
|
|
}, $expectedReturn);
|
|
$actual = $notifier->notifyMentionedUsers($room, $comment, $alreadyNotifiedUsers, false);
|
|
|
|
$this->assertEqualsCanonicalizing($expectedReturn, $actual);
|
|
}
|
|
|
|
public function dataNotifyMentionedUsers(): array {
|
|
return [
|
|
'no notifications' => [
|
|
'No mentions', [], [], [],
|
|
],
|
|
'notify an unotified user' => [
|
|
'Mention @anotherUser', [], ['anotherUser'], ['anotherUser'],
|
|
],
|
|
'not notify mentioned user if already notified' => [
|
|
'Mention @anotherUser', ['anotherUser'], [], ['anotherUser'],
|
|
],
|
|
'notify mentioned Users With Long Message Start Mention' => [
|
|
'123456789 @anotherUserWithOddLengthName 123456789-123456789-123456789-123456789-123456789-123456789', [], ['anotherUserWithOddLengthName'], ['anotherUserWithOddLengthName'],
|
|
],
|
|
'notify mentioned users with long message middle mention' => [
|
|
'123456789-123456789-123456789-1234 @anotherUserWithOddLengthName 6789-123456789-123456789-123456789', [], ['anotherUserWithOddLengthName'], ['anotherUserWithOddLengthName'],
|
|
],
|
|
'notify mentioned users with long message end mention' => [
|
|
'123456789-123456789-123456789-123456789-123456789-123456789 @anotherUserWithOddLengthName 123456789', [], ['anotherUserWithOddLengthName'], ['anotherUserWithOddLengthName'],
|
|
],
|
|
'mention herself' => [
|
|
'Mention @testUser', [], [], [],
|
|
],
|
|
'not notify unknownuser' => [
|
|
'Mention @unknownUser', [], [], [],
|
|
],
|
|
'notify mentioned users several mentions' => [
|
|
'Mention @anotherUser, and @unknownUser, and @testUser, and @userAbleToJoin', [], ['anotherUser', 'userAbleToJoin'], ['anotherUser', 'userAbleToJoin'],
|
|
],
|
|
'notify mentioned users to user not invited to chat' => [
|
|
'Mention @userNotInOneToOneChat', [], [], [],
|
|
]
|
|
];
|
|
}
|
|
|
|
public function dataShouldParticipantBeNotified(): array {
|
|
return [
|
|
[Attendee::ACTOR_GROUPS, 'test1', null, Attendee::ACTOR_USERS, 'test1', [], false],
|
|
[Attendee::ACTOR_USERS, 'test1', null, Attendee::ACTOR_USERS, 'test1', [], false],
|
|
[Attendee::ACTOR_USERS, 'test1', null, Attendee::ACTOR_USERS, 'test2', [], true],
|
|
[Attendee::ACTOR_USERS, 'test1', null, Attendee::ACTOR_USERS, 'test2', [['id' => 'test1', 'type' => Attendee::ACTOR_USERS]], false],
|
|
[Attendee::ACTOR_USERS, 'test1', null, Attendee::ACTOR_USERS, 'test2', [['id' => 'test1', 'type' => Attendee::ACTOR_FEDERATED_USERS]], true],
|
|
[Attendee::ACTOR_USERS, 'test1', Session::SESSION_TIMEOUT - 5, Attendee::ACTOR_USERS, 'test2', [], false],
|
|
[Attendee::ACTOR_USERS, 'test1', Session::SESSION_TIMEOUT + 5, Attendee::ACTOR_USERS, 'test2', [], true],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider dataShouldParticipantBeNotified
|
|
* @param string $actorType
|
|
* @param string $actorId
|
|
* @param int|null $sessionAge
|
|
* @param string $commentActorType
|
|
* @param string $commentActorId
|
|
* @param array $alreadyNotifiedUsers
|
|
* @param bool $expected
|
|
*/
|
|
public function testShouldParticipantBeNotified(string $actorType, string $actorId, ?int $sessionAge, string $commentActorType, string $commentActorId, array $alreadyNotifiedUsers, bool $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,
|
|
]);
|
|
$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->exactly(3))
|
|
->method('setObject')
|
|
->withConsecutive(
|
|
['chat', 'Token123'],
|
|
['room', 'Token123'],
|
|
['call', 'Token123']
|
|
)
|
|
->willReturnSelf();
|
|
|
|
$this->notificationManager->expects($this->exactly(3))
|
|
->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->exactly(1))
|
|
->method('setObject')
|
|
->with('chat', 'Token123')
|
|
->willReturnSelf();
|
|
|
|
$this->notificationManager->expects($this->exactly(1))
|
|
->method('markProcessed')
|
|
->with($notification);
|
|
|
|
$this->getNotifier()->removePendingNotificationsForRoom($room, true);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider dataAddMentionAllToList
|
|
*/
|
|
public function testAddMentionAllToList(array $usersToNotify, array $participants, array $return): void {
|
|
$room = $this->createMock(Room::class);
|
|
$this->participantService
|
|
->method('getActorsByType')
|
|
->willReturn($participants);
|
|
|
|
$actual = $this->invokePrivate($this->getNotifier(), 'addMentionAllToList', [$room, $usersToNotify]);
|
|
$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 function dataAddMentionAllToList(): array {
|
|
return [
|
|
'not notify' => [
|
|
[],
|
|
[],
|
|
[],
|
|
],
|
|
'preserve notify list and do not notify all' => [
|
|
[
|
|
['id' => 'user1', 'type' => Attendee::ACTOR_USERS],
|
|
],
|
|
[],
|
|
[
|
|
['id' => 'user1', 'type' => Attendee::ACTOR_USERS],
|
|
],
|
|
],
|
|
'mention all' => [
|
|
[
|
|
['id' => 'user1', 'type' => Attendee::ACTOR_USERS],
|
|
['id' => 'all'],
|
|
],
|
|
[
|
|
Attendee::fromRow(['actor_id' => 'user1', 'actor_type' => Attendee::ACTOR_USERS]),
|
|
Attendee::fromRow(['actor_id' => 'user2', 'actor_type' => Attendee::ACTOR_USERS]),
|
|
],
|
|
[
|
|
['id' => 'user1', 'type' => Attendee::ACTOR_USERS],
|
|
['id' => 'user2', 'type' => Attendee::ACTOR_USERS],
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @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 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 dataGetMentionedUsers
|
|
*/
|
|
public function testGetMentionedUsers(string $message, array $expectedReturn): void {
|
|
$comment = $this->newComment('108', 'users', 'testUser', new \DateTime('@' . 1000000016), $message);
|
|
$actual = $this->invokePrivate($this->getNotifier(), 'getMentionedUsers', [$comment]);
|
|
$this->assertEqualsCanonicalizing($expectedReturn, $actual);
|
|
}
|
|
|
|
public function dataGetMentionedUsers(): array {
|
|
return [
|
|
'mention one user' => [
|
|
'Mention @anotherUser',
|
|
[
|
|
['id' => 'anotherUser', 'type' => 'users'],
|
|
],
|
|
],
|
|
'mention two user' => [
|
|
'Mention @anotherUser, and @unknownUser',
|
|
[
|
|
['id' => 'anotherUser', 'type' => 'users'],
|
|
['id' => 'unknownUser', 'type' => 'users'],
|
|
],
|
|
],
|
|
'mention all' => [
|
|
'Mention @all',
|
|
[
|
|
['id' => 'all', 'type' => 'users'],
|
|
],
|
|
],
|
|
'mention user, all, guest and group' => [
|
|
'mention @test, @all, @"guest/1" @"group/1"',
|
|
[
|
|
['id' => 'test', 'type' => 'users'],
|
|
['id' => 'all', 'type' => 'users'],
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider dataGetMentionedUserIds
|
|
*/
|
|
public function testGetMentionedUserIds(string $message, array $expectedReturn): void {
|
|
$comment = $this->newComment('108', 'users', 'testUser', new \DateTime('@' . 1000000016), $message);
|
|
$actual = $this->invokePrivate($this->getNotifier(), 'getMentionedUserIds', [$comment]);
|
|
$this->assertEqualsCanonicalizing($expectedReturn, $actual);
|
|
}
|
|
|
|
public function dataGetMentionedUserIds(): array {
|
|
$return = $this->dataGetMentionedUsers();
|
|
array_walk($return, function (array &$scenario) {
|
|
array_walk($scenario[1], function (array &$params) {
|
|
$params = $params['id'];
|
|
});
|
|
return $scenario;
|
|
});
|
|
return $return;
|
|
}
|
|
}
|