<commit_before><?php

declare(strict_types=1);
/**
 * @copyright Copyright (c) 2020 <NAME> <<EMAIL>>
 *
 * @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\Service;

use InvalidArgumentException;
use OC\EventDispatcher\EventDispatcher;
use OCA\Talk\Chat\ChatManager;
use OCA\Talk\Events\VerifyRoomPasswordEvent;
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\ParticipantService;
use OCA\Talk\Service\RoomService;
use OCA\Talk\Webinary;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IDBConnection;
use OCP\IUser;
use OCP\Security\IHasher;
use OCP\Share\IManager as IShareManager;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Test\TestCase;

/**
 * @group DB
 */
class RoomServiceTest extends TestCase {
	/** @var Manager|MockObject */
	protected $manager;
	/** @var ParticipantService|MockObject */
	protected $participantService;
	/** @var IShareManager|MockObject */
	protected $shareManager;
	/** @var IHasher|MockObject */
	protected $hasher;
	/** @var IEventDispatcher|MockObject */
	protected $dispatcher;
	private ?RoomService $service = null;
	/** @var IJobList|MockObject */
	private IJobList $jobList;

	public function setUp(): void {
		parent::setUp();

		$this->manager = $this->createMock(Manager::class);
		$this->chatManager = $this->createMock(ChatManager::class);
		$this->participantService = $this->createMock(ParticipantService::class);
		$this->timeFactory = $this->createMock(ITimeFactory::class);
		$this->shareManager = $this->createMock(IShareManager::class);
		$this->hasher = $this->createMock(IHasher::class);
		$this->dispatcher = $this->createMock(IEventDispatcher::class);
		$this->jobList = $this->createMock(IJobList::class);
		$this->service = new RoomService(
			$this->manager,
			$this->chatManager,
			$this->participantService,
			\OC::$server->get(IDBConnection::class),
			$this->timeFactory,
			$this->shareManager,
			$this->hasher,
			$this->dispatcher,
			$this->jobList
		);
	}

	public function testCreateOneToOneConversationWithSameUser(): void {
		$user = $this->createMock(IUser::class);
		$user->method('getUID')
			->willReturn('uid');

		$this->expectException(InvalidArgumentException::class);
		$this->expectExceptionMessage('invalid_invitee');
		$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,
			], [
				'actorType' => 'users',
				'actorId' => 'uid2',
				'displayName' => 'display-2',
				'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 function dataCreateConversationInvalidNames(): array {
		return [
			[''],
			['        '],
			[str_repeat('a', 256)],
			[str_repeat('😃', 200)],
		];
	}

	/**
	 * @dataProvider dataCreateConversationInvalidNames
	 * @param string $name
	 */<commit_msg>Add test with multibyte emoji<commit_after><?php

declare(strict_types=1);
/**
 * @copyright Copyright (c) 2020 <NAME> <<EMAIL>>
 *
 * @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\Service;

use InvalidArgumentException;
use OC\EventDispatcher\EventDispatcher;
use OCA\Talk\Chat\ChatManager;
use OCA\Talk\Events\VerifyRoomPasswordEvent;
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\ParticipantService;
use OCA\Talk\Service\RoomService;
use OCA\Talk\Webinary;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IDBConnection;
use OCP\IUser;
use OCP\Security\IHasher;
use OCP\Share\IManager as IShareManager;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Test\TestCase;

/**
 * @group DB
 */
class RoomServiceTest extends TestCase {
	/** @var Manager|MockObject */
	protected $manager;
	/** @var ParticipantService|MockObject */
	protected $participantService;
	/** @var IShareManager|MockObject */
	protected $shareManager;
	/** @var IHasher|MockObject */
	protected $hasher;
	/** @var IEventDispatcher|MockObject */
	protected $dispatcher;
	private ?RoomService $service = null;
	/** @var IJobList|MockObject */
	private IJobList $jobList;

	public function setUp(): void {
		parent::setUp();

		$this->manager = $this->createMock(Manager::class);
		$this->chatManager = $this->createMock(ChatManager::class);
		$this->participantService = $this->createMock(ParticipantService::class);
		$this->timeFactory = $this->createMock(ITimeFactory::class);
		$this->shareManager = $this->createMock(IShareManager::class);
		$this->hasher = $this->createMock(IHasher::class);
		$this->dispatcher = $this->createMock(IEventDispatcher::class);
		$this->jobList = $this->createMock(IJobList::class);
		$this->service = new RoomService(
			$this->manager,
			$this->chatManager,
			$this->participantService,
			\OC::$server->get(IDBConnection::class),
			$this->timeFactory,
			$this->shareManager,
			$this->hasher,
			$this->dispatcher,
			$this->jobList
		);
	}

	public function testCreateOneToOneConversationWithSameUser(): void {
		$user = $this->createMock(IUser::class);
		$user->method('getUID')
			->willReturn('uid');

		$this->expectException(InvalidArgumentException::class);
		$this->expectExceptionMessage('invalid_invitee');
		$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,
			], [
				'actorType' => 'users',
				'actorId' => 'uid2',
				'displayName' => 'display-2',
				'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 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
	 * @param string $name
	 */
{'fraction_non_alphanumeric': 0.16563529695411075, 'fraction_numerical': 0.009242144177449169, 'mean_word_length': 5.061373599610326, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 251, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5921321', 'n_tokens_mistral': 4494, 'n_tokens_neox': 4010, 'n_words': 756}