2015-11-23 17:53:55 -05:00
< ? php
2024-05-10 09:09:14 -04:00
/**
* SPDX - FileCopyrightText : 2016 - 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX - FileCopyrightText : 2016 ownCloud , Inc .
* SPDX - License - Identifier : AGPL - 3.0 - only
*/
2015-12-03 11:23:22 -05:00
namespace Test\Comments ;
2016-05-09 04:02:07 -04:00
use OC\Comments\Comment ;
2020-10-22 04:54:03 -04:00
use OC\Comments\Manager ;
2022-03-24 10:57:17 -04:00
use OC\EmojiHelper ;
2020-10-22 04:54:03 -04:00
use OCP\AppFramework\Utility\ITimeFactory ;
2017-08-30 04:56:02 -04:00
use OCP\Comments\IComment ;
2016-05-09 04:02:07 -04:00
use OCP\Comments\ICommentsEventHandler ;
2015-11-23 17:53:55 -05:00
use OCP\Comments\ICommentsManager ;
2017-08-30 04:56:02 -04:00
use OCP\Comments\NotFoundException ;
2024-08-19 10:43:17 -04:00
use OCP\DB\QueryBuilder\IQueryBuilder ;
2024-06-18 08:11:32 -04:00
use OCP\EventDispatcher\IEventDispatcher ;
2024-03-07 09:26:11 -05:00
use OCP\Files\Folder ;
use OCP\Files\IRootFolder ;
2020-10-22 04:54:03 -04:00
use OCP\IConfig ;
2017-03-29 11:29:32 -04:00
use OCP\IDBConnection ;
2020-10-22 04:54:03 -04:00
use OCP\IInitialStateService ;
2016-09-12 14:59:01 -04:00
use OCP\IUser ;
2022-06-13 11:22:36 -04:00
use OCP\Server ;
2020-10-22 04:54:03 -04:00
use Psr\Log\LoggerInterface ;
2015-12-03 11:23:22 -05:00
use Test\TestCase ;
2015-11-23 17:53:55 -05:00
/**
2016-05-18 12:55:44 -04:00
* Class ManagerTest
2015-11-23 17:53:55 -05:00
*
* @ group DB
*/
2016-05-18 12:55:44 -04:00
class ManagerTest extends TestCase {
2017-03-29 11:29:32 -04:00
/** @var IDBConnection */
private $connection ;
2024-03-07 09:26:11 -05:00
/** @var \PHPUnit\Framework\MockObject\MockObject|IRootFolder */
private $rootFolder ;
2015-11-23 17:53:55 -05:00
2019-11-27 09:27:18 -05:00
protected function setUp () : void {
2015-11-23 17:53:55 -05:00
parent :: setUp ();
2017-03-29 11:29:32 -04:00
$this -> connection = \OC :: $server -> getDatabaseConnection ();
2024-03-07 09:26:11 -05:00
$this -> rootFolder = $this -> createMock ( IRootFolder :: class );
2017-03-29 11:29:32 -04:00
$sql = $this -> connection -> getDatabasePlatform () -> getTruncateTableSQL ( '`*PREFIX*comments`' );
$this -> connection -> prepare ( $sql ) -> execute ();
2022-01-11 10:20:51 -05:00
$sql = $this -> connection -> getDatabasePlatform () -> getTruncateTableSQL ( '`*PREFIX*reactions`' );
$this -> connection -> prepare ( $sql ) -> execute ();
2015-11-23 17:53:55 -05:00
}
2022-06-13 11:22:36 -04:00
protected function addDatabaseEntry ( $parentId , $topmostParentId , $creationDT = null , $latestChildDT = null , $objectId = null , $expireDate = null ) {
2017-03-29 11:29:32 -04:00
if ( is_null ( $creationDT )) {
2015-11-23 17:53:55 -05:00
$creationDT = new \DateTime ();
}
2017-03-29 11:29:32 -04:00
if ( is_null ( $latestChildDT )) {
2015-11-23 17:53:55 -05:00
$latestChildDT = new \DateTime ( 'yesterday' );
}
2017-03-29 11:29:32 -04:00
if ( is_null ( $objectId )) {
$objectId = 'file64' ;
}
2015-11-23 17:53:55 -05:00
2017-03-29 11:29:32 -04:00
$qb = $this -> connection -> getQueryBuilder ();
2015-11-23 17:53:55 -05:00
$qb
-> insert ( 'comments' )
-> values ([
2017-03-29 11:29:32 -04:00
'parent_id' => $qb -> createNamedParameter ( $parentId ),
'topmost_parent_id' => $qb -> createNamedParameter ( $topmostParentId ),
'children_count' => $qb -> createNamedParameter ( 2 ),
'actor_type' => $qb -> createNamedParameter ( 'users' ),
'actor_id' => $qb -> createNamedParameter ( 'alice' ),
'message' => $qb -> createNamedParameter ( 'nice one' ),
'verb' => $qb -> createNamedParameter ( 'comment' ),
2024-08-21 05:42:56 -04:00
'creation_timestamp' => $qb -> createNamedParameter ( $creationDT , IQueryBuilder :: PARAM_DATETIME_MUTABLE ),
'latest_child_timestamp' => $qb -> createNamedParameter ( $latestChildDT , IQueryBuilder :: PARAM_DATETIME_MUTABLE ),
2017-03-29 11:29:32 -04:00
'object_type' => $qb -> createNamedParameter ( 'files' ),
'object_id' => $qb -> createNamedParameter ( $objectId ),
2024-08-21 05:42:56 -04:00
'expire_date' => $qb -> createNamedParameter ( $expireDate , IQueryBuilder :: PARAM_DATETIME_MUTABLE ),
2023-12-13 07:29:12 -05:00
'reference_id' => $qb -> createNamedParameter ( 'referenceId' ),
'meta_data' => $qb -> createNamedParameter ( json_encode ([ 'last_edit_actor_id' => 'admin' ])),
2015-11-23 17:53:55 -05:00
])
-> execute ();
2015-12-09 10:33:34 -05:00
return $qb -> getLastInsertId ();
2015-11-23 17:53:55 -05:00
}
protected function getManager () {
2020-10-22 04:54:03 -04:00
return new Manager (
$this -> connection ,
$this -> createMock ( LoggerInterface :: class ),
$this -> createMock ( IConfig :: class ),
$this -> createMock ( ITimeFactory :: class ),
2022-03-24 10:13:09 -04:00
new EmojiHelper ( $this -> connection ),
2024-03-07 09:26:11 -05:00
$this -> createMock ( IInitialStateService :: class ),
$this -> rootFolder ,
2024-06-18 08:11:32 -04:00
$this -> createMock ( IEventDispatcher :: class ),
2020-10-22 04:54:03 -04:00
);
2015-11-23 17:53:55 -05:00
}
2020-08-11 15:32:18 -04:00
2015-11-23 17:53:55 -05:00
public function testGetCommentNotFound () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \OCP\Comments\NotFoundException :: class );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
$manager -> get ( '22' );
}
2020-08-11 15:32:18 -04:00
2015-11-23 17:53:55 -05:00
public function testGetCommentNotFoundInvalidInput () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \InvalidArgumentException :: class );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
$manager -> get ( 'unexisting22' );
}
public function testGetComment () : void {
$manager = $this -> getManager ();
$creationDT = new \DateTime ();
$latestChildDT = new \DateTime ( 'yesterday' );
2015-12-03 11:19:40 -05:00
$qb = \OC :: $server -> getDatabaseConnection () -> getQueryBuilder ();
2015-11-23 17:53:55 -05:00
$qb
-> insert ( 'comments' )
-> values ([
2017-03-29 11:29:32 -04:00
'parent_id' => $qb -> createNamedParameter ( '2' ),
'topmost_parent_id' => $qb -> createNamedParameter ( '1' ),
'children_count' => $qb -> createNamedParameter ( 2 ),
'actor_type' => $qb -> createNamedParameter ( 'users' ),
'actor_id' => $qb -> createNamedParameter ( 'alice' ),
'message' => $qb -> createNamedParameter ( 'nice one' ),
'verb' => $qb -> createNamedParameter ( 'comment' ),
'creation_timestamp' => $qb -> createNamedParameter ( $creationDT , 'datetime' ),
'latest_child_timestamp' => $qb -> createNamedParameter ( $latestChildDT , 'datetime' ),
'object_type' => $qb -> createNamedParameter ( 'files' ),
'object_id' => $qb -> createNamedParameter ( 'file64' ),
2023-12-13 07:29:12 -05:00
'reference_id' => $qb -> createNamedParameter ( 'referenceId' ),
'meta_data' => $qb -> createNamedParameter ( json_encode ([ 'last_edit_actor_id' => 'admin' ])),
2015-11-23 17:53:55 -05:00
])
-> execute ();
2015-12-09 10:33:34 -05:00
$id = strval ( $qb -> getLastInsertId ());
2015-11-23 17:53:55 -05:00
$comment = $manager -> get ( $id );
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $comment instanceof IComment );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $comment -> getId (), $id );
$this -> assertSame ( $comment -> getParentId (), '2' );
$this -> assertSame ( $comment -> getTopmostParentId (), '1' );
$this -> assertSame ( $comment -> getChildrenCount (), 2 );
2016-02-03 13:28:15 -05:00
$this -> assertSame ( $comment -> getActorType (), 'users' );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $comment -> getActorId (), 'alice' );
$this -> assertSame ( $comment -> getMessage (), 'nice one' );
$this -> assertSame ( $comment -> getVerb (), 'comment' );
2016-02-03 13:28:15 -05:00
$this -> assertSame ( $comment -> getObjectType (), 'files' );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $comment -> getObjectId (), 'file64' );
2016-10-21 07:43:39 -04:00
$this -> assertEquals ( $comment -> getCreationDateTime () -> getTimestamp (), $creationDT -> getTimestamp ());
2015-11-23 17:53:55 -05:00
$this -> assertEquals ( $comment -> getLatestChildDateTime (), $latestChildDT );
2023-12-13 07:29:12 -05:00
$this -> assertEquals ( $comment -> getReferenceId (), 'referenceId' );
$this -> assertEquals ( $comment -> getMetaData (), [ 'last_edit_actor_id' => 'admin' ]);
2015-11-23 17:53:55 -05:00
}
2020-08-11 15:32:18 -04:00
2015-11-23 17:53:55 -05:00
public function testGetTreeNotFound () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \OCP\Comments\NotFoundException :: class );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
$manager -> getTree ( '22' );
}
2020-08-11 15:32:18 -04:00
2015-11-23 17:53:55 -05:00
public function testGetTreeNotFoundInvalidIpnut () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \InvalidArgumentException :: class );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
$manager -> getTree ( 'unexisting22' );
}
public function testGetTree () : void {
$headId = $this -> addDatabaseEntry ( 0 , 0 );
$this -> addDatabaseEntry ( $headId , $headId , new \DateTime ( '-3 hours' ));
$this -> addDatabaseEntry ( $headId , $headId , new \DateTime ( '-2 hours' ));
$id = $this -> addDatabaseEntry ( $headId , $headId , new \DateTime ( '-1 hour' ));
$manager = $this -> getManager ();
$tree = $manager -> getTree ( $headId );
// Verifying the root comment
$this -> assertTrue ( isset ( $tree [ 'comment' ]));
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $tree [ 'comment' ] instanceof IComment );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $tree [ 'comment' ] -> getId (), strval ( $headId ));
$this -> assertTrue ( isset ( $tree [ 'replies' ]));
$this -> assertSame ( count ( $tree [ 'replies' ]), 3 );
// one level deep
2017-03-29 11:29:32 -04:00
foreach ( $tree [ 'replies' ] as $reply ) {
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $reply [ 'comment' ] instanceof IComment );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $reply [ 'comment' ] -> getId (), strval ( $id ));
$this -> assertSame ( count ( $reply [ 'replies' ]), 0 );
$id -- ;
}
}
public function testGetTreeNoReplies () : void {
$id = $this -> addDatabaseEntry ( 0 , 0 );
$manager = $this -> getManager ();
$tree = $manager -> getTree ( $id );
// Verifying the root comment
$this -> assertTrue ( isset ( $tree [ 'comment' ]));
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $tree [ 'comment' ] instanceof IComment );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $tree [ 'comment' ] -> getId (), strval ( $id ));
$this -> assertTrue ( isset ( $tree [ 'replies' ]));
$this -> assertSame ( count ( $tree [ 'replies' ]), 0 );
// one level deep
2017-03-29 11:29:32 -04:00
foreach ( $tree [ 'replies' ] as $reply ) {
2015-11-23 17:53:55 -05:00
throw new \Exception ( 'This ain`t happen' );
}
}
public function testGetTreeWithLimitAndOffset () : void {
$headId = $this -> addDatabaseEntry ( 0 , 0 );
$this -> addDatabaseEntry ( $headId , $headId , new \DateTime ( '-3 hours' ));
$this -> addDatabaseEntry ( $headId , $headId , new \DateTime ( '-2 hours' ));
$this -> addDatabaseEntry ( $headId , $headId , new \DateTime ( '-1 hour' ));
$idToVerify = $this -> addDatabaseEntry ( $headId , $headId , new \DateTime ());
$manager = $this -> getManager ();
for ( $offset = 0 ; $offset < 3 ; $offset += 2 ) {
$tree = $manager -> getTree ( strval ( $headId ), 2 , $offset );
// Verifying the root comment
$this -> assertTrue ( isset ( $tree [ 'comment' ]));
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $tree [ 'comment' ] instanceof IComment );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $tree [ 'comment' ] -> getId (), strval ( $headId ));
$this -> assertTrue ( isset ( $tree [ 'replies' ]));
$this -> assertSame ( count ( $tree [ 'replies' ]), 2 );
// one level deep
foreach ( $tree [ 'replies' ] as $reply ) {
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $reply [ 'comment' ] instanceof IComment );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $reply [ 'comment' ] -> getId (), strval ( $idToVerify ));
$this -> assertSame ( count ( $reply [ 'replies' ]), 0 );
$idToVerify -- ;
}
}
}
public function testGetForObject () : void {
$this -> addDatabaseEntry ( 0 , 0 );
$manager = $this -> getManager ();
2016-02-03 13:28:15 -05:00
$comments = $manager -> getForObject ( 'files' , 'file64' );
2015-11-23 17:53:55 -05:00
$this -> assertTrue ( is_array ( $comments ));
$this -> assertSame ( count ( $comments ), 1 );
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $comments [ 0 ] instanceof IComment );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $comments [ 0 ] -> getMessage (), 'nice one' );
}
public function testGetForObjectWithLimitAndOffset () : void {
$this -> addDatabaseEntry ( 0 , 0 , new \DateTime ( '-6 hours' ));
$this -> addDatabaseEntry ( 0 , 0 , new \DateTime ( '-5 hours' ));
$this -> addDatabaseEntry ( 1 , 1 , new \DateTime ( '-4 hours' ));
$this -> addDatabaseEntry ( 0 , 0 , new \DateTime ( '-3 hours' ));
$this -> addDatabaseEntry ( 2 , 2 , new \DateTime ( '-2 hours' ));
$this -> addDatabaseEntry ( 2 , 2 , new \DateTime ( '-1 hours' ));
$idToVerify = $this -> addDatabaseEntry ( 3 , 1 , new \DateTime ());
$manager = $this -> getManager ();
$offset = 0 ;
do {
2016-02-03 13:28:15 -05:00
$comments = $manager -> getForObject ( 'files' , 'file64' , 3 , $offset );
2015-11-23 17:53:55 -05:00
$this -> assertTrue ( is_array ( $comments ));
2017-03-29 11:29:32 -04:00
foreach ( $comments as $comment ) {
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $comment instanceof IComment );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $comment -> getMessage (), 'nice one' );
$this -> assertSame ( $comment -> getId (), strval ( $idToVerify ));
$idToVerify -- ;
}
$offset += 3 ;
2017-03-29 11:29:32 -04:00
} while ( count ( $comments ) > 0 );
2015-11-23 17:53:55 -05:00
}
public function testGetForObjectWithDateTimeConstraint () : void {
$this -> addDatabaseEntry ( 0 , 0 , new \DateTime ( '-6 hours' ));
$this -> addDatabaseEntry ( 0 , 0 , new \DateTime ( '-5 hours' ));
$id1 = $this -> addDatabaseEntry ( 0 , 0 , new \DateTime ( '-3 hours' ));
$id2 = $this -> addDatabaseEntry ( 2 , 2 , new \DateTime ( '-2 hours' ));
$manager = $this -> getManager ();
2016-02-03 13:28:15 -05:00
$comments = $manager -> getForObject ( 'files' , 'file64' , 0 , 0 , new \DateTime ( '-4 hours' ));
2015-11-23 17:53:55 -05:00
$this -> assertSame ( count ( $comments ), 2 );
$this -> assertSame ( $comments [ 0 ] -> getId (), strval ( $id2 ));
$this -> assertSame ( $comments [ 1 ] -> getId (), strval ( $id1 ));
}
public function testGetForObjectWithLimitAndOffsetAndDateTimeConstraint () : void {
$this -> addDatabaseEntry ( 0 , 0 , new \DateTime ( '-7 hours' ));
$this -> addDatabaseEntry ( 0 , 0 , new \DateTime ( '-6 hours' ));
$this -> addDatabaseEntry ( 1 , 1 , new \DateTime ( '-5 hours' ));
$this -> addDatabaseEntry ( 0 , 0 , new \DateTime ( '-3 hours' ));
$this -> addDatabaseEntry ( 2 , 2 , new \DateTime ( '-2 hours' ));
$this -> addDatabaseEntry ( 2 , 2 , new \DateTime ( '-1 hours' ));
$idToVerify = $this -> addDatabaseEntry ( 3 , 1 , new \DateTime ());
$manager = $this -> getManager ();
$offset = 0 ;
do {
2016-02-03 13:28:15 -05:00
$comments = $manager -> getForObject ( 'files' , 'file64' , 3 , $offset , new \DateTime ( '-4 hours' ));
2015-11-23 17:53:55 -05:00
$this -> assertTrue ( is_array ( $comments ));
2017-03-29 11:29:32 -04:00
foreach ( $comments as $comment ) {
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $comment instanceof IComment );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $comment -> getMessage (), 'nice one' );
$this -> assertSame ( $comment -> getId (), strval ( $idToVerify ));
$this -> assertTrue ( intval ( $comment -> getId ()) >= 4 );
$idToVerify -- ;
}
$offset += 3 ;
2017-03-29 11:29:32 -04:00
} while ( count ( $comments ) > 0 );
2015-11-23 17:53:55 -05:00
}
public function testGetNumberOfCommentsForObject () : void {
2017-03-29 11:29:32 -04:00
for ( $i = 1 ; $i < 5 ; $i ++ ) {
2015-11-23 17:53:55 -05:00
$this -> addDatabaseEntry ( 0 , 0 );
}
$manager = $this -> getManager ();
$amount = $manager -> getNumberOfCommentsForObject ( 'untype' , '00' );
$this -> assertSame ( $amount , 0 );
2016-02-03 13:28:15 -05:00
$amount = $manager -> getNumberOfCommentsForObject ( 'files' , 'file64' );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $amount , 4 );
}
2017-03-29 11:29:32 -04:00
public function testGetNumberOfUnreadCommentsForFolder () : void {
2024-03-07 09:26:11 -05:00
$folder = $this -> createMock ( Folder :: class );
$fileIds = range ( 1111 , 1114 );
$children = array_map ( function ( int $id ) {
$file = $this -> createMock ( Folder :: class );
$file -> method ( 'getId' )
-> willReturn ( $id );
return $file ;
}, $fileIds );
$folder -> method ( 'getId' ) -> willReturn ( 1000 );
$folder -> method ( 'getDirectoryListing' ) -> willReturn ( $children );
$this -> rootFolder -> method ( 'getFirstNodeById' )
-> with ( $folder -> getId ())
-> willReturn ( $folder );
2017-03-29 11:29:32 -04:00
2017-07-22 06:21:00 -04:00
// 2 comment for 1111 with 1 before read marker
// 2 comments for 1112 with no read marker
// 1 comment for 1113 before read marker
// 1 comment for 1114 with no read marker
$this -> addDatabaseEntry ( 0 , 0 , null , null , $fileIds [ 1 ]);
for ( $i = 0 ; $i < 4 ; $i ++ ) {
$this -> addDatabaseEntry ( 0 , 0 , null , null , $fileIds [ $i ]);
}
$this -> addDatabaseEntry ( 0 , 0 , ( new \DateTime ()) -> modify ( '-2 days' ), null , $fileIds [ 0 ]);
2020-08-11 15:32:18 -04:00
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
2017-07-22 06:21:00 -04:00
$user = $this -> createMock ( IUser :: class );
$user -> expects ( $this -> any ())
-> method ( 'getUID' )
2020-03-25 17:21:27 -04:00
-> willReturn ( 'comment_test' );
2017-07-22 06:21:00 -04:00
$manager = $this -> getManager ();
$manager -> setReadMark ( 'files' , ( string ) $fileIds [ 0 ], ( new \DateTime ()) -> modify ( '-1 days' ), $user );
$manager -> setReadMark ( 'files' , ( string ) $fileIds [ 2 ], ( new \DateTime ()), $user );
2024-03-07 09:26:11 -05:00
$amount = $manager -> getNumberOfUnreadCommentsForFolder ( $folder -> getId (), $user );
2017-03-29 11:29:32 -04:00
$this -> assertEquals ([
2017-07-22 06:21:00 -04:00
$fileIds [ 0 ] => 1 ,
$fileIds [ 1 ] => 2 ,
$fileIds [ 3 ] => 1 ,
2017-03-29 11:29:32 -04:00
], $amount );
}
2018-04-10 04:50:57 -04:00
/**
* @ dataProvider dataGetForObjectSince
* @ param $lastKnown
* @ param $order
* @ param $limit
* @ param $resultFrom
* @ param $resultTo
*/
public function testGetForObjectSince ( $lastKnown , $order , $limit , $resultFrom , $resultTo ) : void {
$ids = [];
$ids [] = $this -> addDatabaseEntry ( 0 , 0 );
$ids [] = $this -> addDatabaseEntry ( 0 , 0 );
$ids [] = $this -> addDatabaseEntry ( 0 , 0 );
$ids [] = $this -> addDatabaseEntry ( 0 , 0 );
$ids [] = $this -> addDatabaseEntry ( 0 , 0 );
$manager = $this -> getManager ();
$comments = $manager -> getForObjectSince ( 'files' , 'file64' , ( $lastKnown === null ? 0 : $ids [ $lastKnown ]), $order , $limit );
$expected = array_slice ( $ids , $resultFrom , $resultTo - $resultFrom + 1 );
if ( $order === 'desc' ) {
$expected = array_reverse ( $expected );
}
2020-04-09 07:53:40 -04:00
$this -> assertSame ( $expected , array_map ( function ( IComment $c ) {
2018-04-10 04:50:57 -04:00
return ( int ) $c -> getId ();
}, $comments ));
}
public function dataGetForObjectSince () {
return [
[ null , 'asc' , 20 , 0 , 4 ],
[ null , 'asc' , 2 , 0 , 1 ],
[ null , 'desc' , 20 , 0 , 4 ],
[ null , 'desc' , 2 , 3 , 4 ],
[ 1 , 'asc' , 20 , 2 , 4 ],
[ 1 , 'asc' , 2 , 2 , 3 ],
[ 3 , 'desc' , 20 , 0 , 2 ],
[ 3 , 'desc' , 2 , 1 , 2 ],
];
}
2015-11-23 17:53:55 -05:00
public function invalidCreateArgsProvider () {
return [
[ '' , 'aId-1' , 'oType-1' , 'oId-1' ],
[ 'aType-1' , '' , 'oType-1' , 'oId-1' ],
[ 'aType-1' , 'aId-1' , '' , 'oId-1' ],
[ 'aType-1' , 'aId-1' , 'oType-1' , '' ],
[ 1 , 'aId-1' , 'oType-1' , 'oId-1' ],
[ 'aType-1' , 1 , 'oType-1' , 'oId-1' ],
[ 'aType-1' , 'aId-1' , 1 , 'oId-1' ],
[ 'aType-1' , 'aId-1' , 'oType-1' , 1 ],
];
}
/**
* @ dataProvider invalidCreateArgsProvider
2017-08-30 04:56:02 -04:00
* @ param string $aType
* @ param string $aId
* @ param string $oType
* @ param string $oId
2015-11-23 17:53:55 -05:00
*/
public function testCreateCommentInvalidArguments ( $aType , $aId , $oType , $oId ) : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \InvalidArgumentException :: class );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
$manager -> create ( $aType , $aId , $oType , $oId );
}
public function testCreateComment () : void {
$actorType = 'bot' ;
$actorId = 'bob' ;
$objectType = 'weather' ;
$objectId = 'bielefeld' ;
$comment = $this -> getManager () -> create ( $actorType , $actorId , $objectType , $objectId );
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $comment instanceof IComment );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $comment -> getActorType (), $actorType );
$this -> assertSame ( $comment -> getActorId (), $actorId );
$this -> assertSame ( $comment -> getObjectType (), $objectType );
$this -> assertSame ( $comment -> getObjectId (), $objectId );
}
2020-08-11 15:32:18 -04:00
2015-11-23 17:53:55 -05:00
public function testDelete () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \OCP\Comments\NotFoundException :: class );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
$done = $manager -> delete ( '404' );
$this -> assertFalse ( $done );
$done = $manager -> delete ( '%' );
$this -> assertFalse ( $done );
$done = $manager -> delete ( '' );
$this -> assertFalse ( $done );
$id = strval ( $this -> addDatabaseEntry ( 0 , 0 ));
$comment = $manager -> get ( $id );
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $comment instanceof IComment );
2015-11-23 17:53:55 -05:00
$done = $manager -> delete ( $id );
$this -> assertTrue ( $done );
$manager -> get ( $id );
}
2022-01-11 10:20:51 -05:00
/**
2022-01-21 06:40:29 -05:00
* @ dataProvider providerTestSave
2022-01-11 10:20:51 -05:00
*/
2022-01-21 06:40:29 -05:00
public function testSave ( string $message , string $actorId , string $verb , ? string $parentId , ? string $id = '' ) : IComment {
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
2016-05-09 04:02:07 -04:00
$comment = new Comment ();
2015-11-23 17:53:55 -05:00
$comment
2022-01-21 06:40:29 -05:00
-> setId ( $id )
2022-01-11 10:20:51 -05:00
-> setActor ( 'users' , $actorId )
2016-02-03 13:28:15 -05:00
-> setObject ( 'files' , 'file64' )
2022-01-11 10:20:51 -05:00
-> setMessage ( $message )
-> setVerb ( $verb );
if ( $parentId ) {
$comment -> setParentId ( $parentId );
}
2015-11-23 17:53:55 -05:00
$saveSuccessful = $manager -> save ( $comment );
2025-04-09 07:49:16 -04:00
$this -> assertTrue ( $saveSuccessful , 'Comment saving was not successful' );
$this -> assertNotEquals ( '' , $comment -> getId (), 'Comment ID should not be empty' );
$this -> assertNotEquals ( '0' , $comment -> getId (), 'Comment ID should not be string \'0\'' );
$this -> assertNotNull ( $comment -> getCreationDateTime (), 'Comment creation date should not be null' );
2015-11-23 17:53:55 -05:00
$loadedComment = $manager -> get ( $comment -> getId ());
2025-04-09 07:49:16 -04:00
$this -> assertSame ( $comment -> getMessage (), $loadedComment -> getMessage (), 'Comment message should match' );
$this -> assertEquals ( $comment -> getCreationDateTime () -> getTimestamp (), $loadedComment -> getCreationDateTime () -> getTimestamp (), 'Comment creation date should match' );
2022-01-11 10:20:51 -05:00
return $comment ;
}
2022-01-21 06:40:29 -05:00
public function providerTestSave () : array {
2022-01-11 10:20:51 -05:00
return [
[ 'very beautiful, I am impressed!' , 'alice' , 'comment' , null ]
];
2015-11-23 17:53:55 -05:00
}
public function testSaveUpdate () : void {
$manager = $this -> getManager ();
2016-05-09 04:02:07 -04:00
$comment = new Comment ();
2015-11-23 17:53:55 -05:00
$comment
2017-03-29 11:29:32 -04:00
-> setActor ( 'users' , 'alice' )
-> setObject ( 'files' , 'file64' )
-> setMessage ( 'very beautiful, I am impressed!' )
2023-04-26 12:17:37 -04:00
-> setVerb ( 'comment' )
-> setExpireDate ( new \DateTime ( '+2 hours' ));
2015-11-23 17:53:55 -05:00
$manager -> save ( $comment );
2023-04-26 12:17:37 -04:00
$loadedComment = $manager -> get ( $comment -> getId ());
// Compare current object with database values
$this -> assertSame ( $comment -> getMessage (), $loadedComment -> getMessage ());
$this -> assertSame (
$comment -> getExpireDate () -> format ( 'Y-m-d H:i:s' ),
$loadedComment -> getExpireDate () -> format ( 'Y-m-d H:i:s' )
);
// Preserve the original comment to compare after update
$original = clone $comment ;
// Update values
$comment -> setMessage ( 'very beautiful, I am really so much impressed!' )
-> setExpireDate ( new \DateTime ( '+1 hours' ));
2015-11-23 17:53:55 -05:00
$manager -> save ( $comment );
$loadedComment = $manager -> get ( $comment -> getId ());
2023-04-26 12:17:37 -04:00
// Compare current object with database values
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $comment -> getMessage (), $loadedComment -> getMessage ());
2023-04-26 12:17:37 -04:00
$this -> assertSame (
$comment -> getExpireDate () -> format ( 'Y-m-d H:i:s' ),
$loadedComment -> getExpireDate () -> format ( 'Y-m-d H:i:s' )
);
// Compare original object with database values
$this -> assertNotSame ( $original -> getMessage (), $loadedComment -> getMessage ());
$this -> assertNotSame (
$original -> getExpireDate () -> format ( 'Y-m-d H:i:s' ),
$loadedComment -> getExpireDate () -> format ( 'Y-m-d H:i:s' )
);
2015-11-23 17:53:55 -05:00
}
2020-08-11 15:32:18 -04:00
2015-11-23 17:53:55 -05:00
public function testSaveUpdateException () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \OCP\Comments\NotFoundException :: class );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
2016-05-09 04:02:07 -04:00
$comment = new Comment ();
2015-11-23 17:53:55 -05:00
$comment
2017-03-29 11:29:32 -04:00
-> setActor ( 'users' , 'alice' )
-> setObject ( 'files' , 'file64' )
-> setMessage ( 'very beautiful, I am impressed!' )
-> setVerb ( 'comment' );
2015-11-23 17:53:55 -05:00
$manager -> save ( $comment );
$manager -> delete ( $comment -> getId ());
$comment -> setMessage ( 'very beautiful, I am really so much impressed!' );
$manager -> save ( $comment );
}
2020-08-11 15:32:18 -04:00
2015-11-23 17:53:55 -05:00
public function testSaveIncomplete () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \UnexpectedValueException :: class );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
2016-05-09 04:02:07 -04:00
$comment = new Comment ();
2015-11-23 17:53:55 -05:00
$comment -> setMessage ( 'from no one to nothing' );
$manager -> save ( $comment );
}
public function testSaveAsChild () : void {
$id = $this -> addDatabaseEntry ( 0 , 0 );
$manager = $this -> getManager ();
2017-03-29 11:29:32 -04:00
for ( $i = 0 ; $i < 3 ; $i ++ ) {
2016-05-09 04:02:07 -04:00
$comment = new Comment ();
2015-11-23 17:53:55 -05:00
$comment
2017-03-29 11:29:32 -04:00
-> setActor ( 'users' , 'alice' )
-> setObject ( 'files' , 'file64' )
-> setParentId ( strval ( $id ))
-> setMessage ( 'full ack' )
-> setVerb ( 'comment' )
// setting the creation time avoids using sleep() while making sure to test with different timestamps
-> setCreationDateTime ( new \DateTime ( '+' . $i . ' minutes' ));
2015-11-23 17:53:55 -05:00
$manager -> save ( $comment );
$this -> assertSame ( $comment -> getTopmostParentId (), strval ( $id ));
$parentComment = $manager -> get ( strval ( $id ));
$this -> assertSame ( $parentComment -> getChildrenCount (), $i + 1 );
2016-10-21 07:43:39 -04:00
$this -> assertEquals ( $parentComment -> getLatestChildDateTime () -> getTimestamp (), $comment -> getCreationDateTime () -> getTimestamp ());
2015-11-23 17:53:55 -05:00
}
}
public function invalidActorArgsProvider () {
return
2017-03-29 11:29:32 -04:00
[
[ '' , '' ],
[ 1 , 'alice' ],
[ 'users' , 1 ],
];
2015-11-23 17:53:55 -05:00
}
/**
* @ dataProvider invalidActorArgsProvider
2017-08-30 04:56:02 -04:00
* @ param string $type
* @ param string $id
2015-11-23 17:53:55 -05:00
*/
public function testDeleteReferencesOfActorInvalidInput ( $type , $id ) : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \InvalidArgumentException :: class );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
$manager -> deleteReferencesOfActor ( $type , $id );
}
public function testDeleteReferencesOfActor () : void {
$ids = [];
$ids [] = $this -> addDatabaseEntry ( 0 , 0 );
$ids [] = $this -> addDatabaseEntry ( 0 , 0 );
$ids [] = $this -> addDatabaseEntry ( 0 , 0 );
$manager = $this -> getManager ();
// just to make sure they are really set, with correct actor data
$comment = $manager -> get ( strval ( $ids [ 1 ]));
2016-02-03 13:28:15 -05:00
$this -> assertSame ( $comment -> getActorType (), 'users' );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $comment -> getActorId (), 'alice' );
2016-02-03 13:28:15 -05:00
$wasSuccessful = $manager -> deleteReferencesOfActor ( 'users' , 'alice' );
2015-11-23 17:53:55 -05:00
$this -> assertTrue ( $wasSuccessful );
2017-03-29 11:29:32 -04:00
foreach ( $ids as $id ) {
2015-11-23 17:53:55 -05:00
$comment = $manager -> get ( strval ( $id ));
$this -> assertSame ( $comment -> getActorType (), ICommentsManager :: DELETED_USER );
$this -> assertSame ( $comment -> getActorId (), ICommentsManager :: DELETED_USER );
}
// actor info is gone from DB, but when database interaction is alright,
// we still expect to get true back
2016-02-03 13:28:15 -05:00
$wasSuccessful = $manager -> deleteReferencesOfActor ( 'users' , 'alice' );
2015-11-23 17:53:55 -05:00
$this -> assertTrue ( $wasSuccessful );
}
public function testDeleteReferencesOfActorWithUserManagement () : void {
2024-09-15 07:43:03 -04:00
$user = \OC :: $server -> getUserManager () -> createUser ( 'xenia' , 'NotAnEasyPassword123456+' );
2017-08-30 04:56:02 -04:00
$this -> assertTrue ( $user instanceof IUser );
2015-11-23 17:53:55 -05:00
2023-08-29 18:30:52 -04:00
$manager = \OC :: $server -> get ( ICommentsManager :: class );
2016-02-03 13:28:15 -05:00
$comment = $manager -> create ( 'users' , $user -> getUID (), 'files' , 'file64' );
2015-11-23 17:53:55 -05:00
$comment
-> setMessage ( 'Most important comment I ever left on the Internet.' )
-> setVerb ( 'comment' );
$status = $manager -> save ( $comment );
$this -> assertTrue ( $status );
$commentID = $comment -> getId ();
$user -> delete ();
2015-12-03 10:35:57 -05:00
$comment = $manager -> get ( $commentID );
2017-08-30 04:56:02 -04:00
$this -> assertSame ( $comment -> getActorType (), ICommentsManager :: DELETED_USER );
$this -> assertSame ( $comment -> getActorId (), ICommentsManager :: DELETED_USER );
2015-11-23 17:53:55 -05:00
}
public function invalidObjectArgsProvider () {
return
2017-03-29 11:29:32 -04:00
[
[ '' , '' ],
[ 1 , 'file64' ],
[ 'files' , 1 ],
];
2015-11-23 17:53:55 -05:00
}
/**
* @ dataProvider invalidObjectArgsProvider
2017-08-30 04:56:02 -04:00
* @ param string $type
* @ param string $id
2015-11-23 17:53:55 -05:00
*/
public function testDeleteCommentsAtObjectInvalidInput ( $type , $id ) : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \InvalidArgumentException :: class );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
$manager -> deleteCommentsAtObject ( $type , $id );
}
public function testDeleteCommentsAtObject () : void {
$ids = [];
$ids [] = $this -> addDatabaseEntry ( 0 , 0 );
$ids [] = $this -> addDatabaseEntry ( 0 , 0 );
$ids [] = $this -> addDatabaseEntry ( 0 , 0 );
$manager = $this -> getManager ();
// just to make sure they are really set, with correct actor data
$comment = $manager -> get ( strval ( $ids [ 1 ]));
2016-02-03 13:28:15 -05:00
$this -> assertSame ( $comment -> getObjectType (), 'files' );
2015-11-23 17:53:55 -05:00
$this -> assertSame ( $comment -> getObjectId (), 'file64' );
2016-02-03 13:28:15 -05:00
$wasSuccessful = $manager -> deleteCommentsAtObject ( 'files' , 'file64' );
2015-11-23 17:53:55 -05:00
$this -> assertTrue ( $wasSuccessful );
$verified = 0 ;
2017-03-29 11:29:32 -04:00
foreach ( $ids as $id ) {
2015-11-23 17:53:55 -05:00
try {
$manager -> get ( strval ( $id ));
2017-08-30 04:56:02 -04:00
} catch ( NotFoundException $e ) {
2015-11-23 17:53:55 -05:00
$verified ++ ;
}
}
$this -> assertSame ( $verified , 3 );
// actor info is gone from DB, but when database interaction is alright,
// we still expect to get true back
2016-02-03 13:28:15 -05:00
$wasSuccessful = $manager -> deleteCommentsAtObject ( 'files' , 'file64' );
2015-11-23 17:53:55 -05:00
$this -> assertTrue ( $wasSuccessful );
}
2022-07-01 09:53:37 -04:00
public function testDeleteCommentsExpiredAtObjectTypeAndId () : void {
2022-06-13 11:22:36 -04:00
$ids = [];
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , null , new \DateTime ( '+2 hours' ));
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , null , new \DateTime ( '+2 hours' ));
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , null , new \DateTime ( '+2 hours' ));
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , null , new \DateTime ( '-2 hours' ));
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , null , new \DateTime ( '-2 hours' ));
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , null , new \DateTime ( '-2 hours' ));
$manager = new Manager (
$this -> connection ,
$this -> createMock ( LoggerInterface :: class ),
$this -> createMock ( IConfig :: class ),
Server :: get ( ITimeFactory :: class ),
new EmojiHelper ( $this -> connection ),
2024-03-07 09:26:11 -05:00
$this -> createMock ( IInitialStateService :: class ),
$this -> rootFolder ,
2024-06-18 08:11:32 -04:00
$this -> createMock ( IEventDispatcher :: class )
2022-06-13 11:22:36 -04:00
);
// just to make sure they are really set, with correct actor data
2022-06-20 10:46:13 -04:00
$comment = $manager -> get (( string ) $ids [ 1 ]);
2022-06-13 11:22:36 -04:00
$this -> assertSame ( $comment -> getObjectType (), 'files' );
$this -> assertSame ( $comment -> getObjectId (), 'file64' );
2022-07-01 09:53:37 -04:00
$deleted = $manager -> deleteCommentsExpiredAtObject ( 'files' , 'file64' );
2022-06-13 11:22:36 -04:00
$this -> assertTrue ( $deleted );
$deleted = 0 ;
$exists = 0 ;
foreach ( $ids as $id ) {
try {
2022-06-20 10:46:13 -04:00
$manager -> get (( string ) $id );
2022-06-13 11:22:36 -04:00
$exists ++ ;
} catch ( NotFoundException $e ) {
$deleted ++ ;
}
}
$this -> assertSame ( $exists , 3 );
$this -> assertSame ( $deleted , 3 );
// actor info is gone from DB, but when database interaction is alright,
// we still expect to get true back
2022-07-01 09:53:37 -04:00
$deleted = $manager -> deleteCommentsExpiredAtObject ( 'files' , 'file64' );
$this -> assertFalse ( $deleted );
}
public function testDeleteCommentsExpiredAtObjectType () : void {
$ids = [];
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , 'file1' , new \DateTime ( '-2 hours' ));
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , 'file2' , new \DateTime ( '-2 hours' ));
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , 'file3' , new \DateTime ( '-2 hours' ));
2022-07-25 10:30:20 -04:00
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , 'file3' , new \DateTime ());
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , 'file3' , new \DateTime ());
$ids [] = $this -> addDatabaseEntry ( 0 , 0 , null , null , 'file3' , new \DateTime ());
2022-07-01 09:53:37 -04:00
$manager = new Manager (
$this -> connection ,
$this -> createMock ( LoggerInterface :: class ),
$this -> createMock ( IConfig :: class ),
Server :: get ( ITimeFactory :: class ),
new EmojiHelper ( $this -> connection ),
2024-03-07 09:26:11 -05:00
$this -> createMock ( IInitialStateService :: class ),
$this -> rootFolder ,
2024-06-18 08:11:32 -04:00
$this -> createMock ( IEventDispatcher :: class )
2022-07-01 09:53:37 -04:00
);
$deleted = $manager -> deleteCommentsExpiredAtObject ( 'files' );
$this -> assertTrue ( $deleted );
$deleted = 0 ;
$exists = 0 ;
foreach ( $ids as $id ) {
try {
$manager -> get (( string ) $id );
$exists ++ ;
} catch ( NotFoundException $e ) {
$deleted ++ ;
}
}
$this -> assertSame ( $exists , 0 );
2022-07-25 10:30:20 -04:00
$this -> assertSame ( $deleted , 6 );
2022-07-01 09:53:37 -04:00
// actor info is gone from DB, but when database interaction is alright,
// we still expect to get true back
$deleted = $manager -> deleteCommentsExpiredAtObject ( 'files' );
2022-06-13 11:22:36 -04:00
$this -> assertFalse ( $deleted );
}
2016-01-27 12:30:09 -05:00
public function testSetMarkRead () : void {
2020-08-11 15:32:18 -04:00
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
2016-09-12 14:59:01 -04:00
$user = $this -> createMock ( IUser :: class );
2016-01-27 12:30:09 -05:00
$user -> expects ( $this -> any ())
-> method ( 'getUID' )
2020-03-25 17:21:27 -04:00
-> willReturn ( 'alice' );
2016-01-27 12:30:09 -05:00
$dateTimeSet = new \DateTime ();
$manager = $this -> getManager ();
2016-01-28 16:59:48 -05:00
$manager -> setReadMark ( 'robot' , '36' , $dateTimeSet , $user );
2016-01-27 12:30:09 -05:00
2017-03-29 11:29:32 -04:00
$dateTimeGet = $manager -> getReadMark ( 'robot' , '36' , $user );
2016-01-27 12:30:09 -05:00
2016-10-21 07:43:39 -04:00
$this -> assertEquals ( $dateTimeGet -> getTimestamp (), $dateTimeSet -> getTimestamp ());
2016-01-27 12:30:09 -05:00
}
2016-01-28 16:59:48 -05:00
public function testSetMarkReadUpdate () : void {
2020-08-11 15:32:18 -04:00
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
2016-09-12 14:59:01 -04:00
$user = $this -> createMock ( IUser :: class );
2016-01-28 16:59:48 -05:00
$user -> expects ( $this -> any ())
-> method ( 'getUID' )
2020-03-25 17:21:27 -04:00
-> willReturn ( 'alice' );
2016-01-28 16:59:48 -05:00
$dateTimeSet = new \DateTime ( 'yesterday' );
$manager = $this -> getManager ();
$manager -> setReadMark ( 'robot' , '36' , $dateTimeSet , $user );
$dateTimeSet = new \DateTime ( 'today' );
$manager -> setReadMark ( 'robot' , '36' , $dateTimeSet , $user );
2017-03-29 11:29:32 -04:00
$dateTimeGet = $manager -> getReadMark ( 'robot' , '36' , $user );
2016-01-28 16:59:48 -05:00
$this -> assertEquals ( $dateTimeGet , $dateTimeSet );
}
public function testReadMarkDeleteUser () : void {
2020-08-11 15:32:18 -04:00
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
2016-09-12 14:59:01 -04:00
$user = $this -> createMock ( IUser :: class );
2016-01-28 16:59:48 -05:00
$user -> expects ( $this -> any ())
-> method ( 'getUID' )
2020-03-25 17:21:27 -04:00
-> willReturn ( 'alice' );
2016-01-28 16:59:48 -05:00
$dateTimeSet = new \DateTime ();
$manager = $this -> getManager ();
$manager -> setReadMark ( 'robot' , '36' , $dateTimeSet , $user );
$manager -> deleteReadMarksFromUser ( $user );
2017-03-29 11:29:32 -04:00
$dateTimeGet = $manager -> getReadMark ( 'robot' , '36' , $user );
2016-01-28 16:59:48 -05:00
$this -> assertNull ( $dateTimeGet );
}
public function testReadMarkDeleteObject () : void {
2020-08-11 15:32:18 -04:00
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
2016-09-12 14:59:01 -04:00
$user = $this -> createMock ( IUser :: class );
2016-01-28 16:59:48 -05:00
$user -> expects ( $this -> any ())
-> method ( 'getUID' )
2020-03-25 17:21:27 -04:00
-> willReturn ( 'alice' );
2016-01-28 16:59:48 -05:00
$dateTimeSet = new \DateTime ();
$manager = $this -> getManager ();
$manager -> setReadMark ( 'robot' , '36' , $dateTimeSet , $user );
$manager -> deleteReadMarksOnObject ( 'robot' , '36' );
2017-03-29 11:29:32 -04:00
$dateTimeGet = $manager -> getReadMark ( 'robot' , '36' , $user );
2016-01-28 16:59:48 -05:00
$this -> assertNull ( $dateTimeGet );
}
2016-05-09 04:02:07 -04:00
public function testSendEvent () : void {
$handler1 = $this -> getMockBuilder ( ICommentsEventHandler :: class ) -> getMock ();
2016-10-12 12:06:22 -04:00
$handler1 -> expects ( $this -> exactly ( 4 ))
2016-05-09 04:02:07 -04:00
-> method ( 'handle' );
$handler2 = $this -> getMockBuilder ( ICommentsEventHandler :: class ) -> getMock ();
2016-10-12 12:06:22 -04:00
$handler1 -> expects ( $this -> exactly ( 4 ))
2016-05-09 04:02:07 -04:00
-> method ( 'handle' );
$manager = $this -> getManager ();
2017-03-29 11:29:32 -04:00
$manager -> registerEventHandler ( function () use ( $handler1 ) {
return $handler1 ;
});
$manager -> registerEventHandler ( function () use ( $handler2 ) {
return $handler2 ;
});
2016-05-09 04:02:07 -04:00
$comment = new Comment ();
$comment
-> setActor ( 'users' , 'alice' )
-> setObject ( 'files' , 'file64' )
-> setMessage ( 'very beautiful, I am impressed!' )
-> setVerb ( 'comment' );
// Add event
$manager -> save ( $comment );
// Update event
$comment -> setMessage ( 'Different topic' );
$manager -> save ( $comment );
// Delete event
$manager -> delete ( $comment -> getId ());
}
2016-10-16 14:28:36 -04:00
public function testResolveDisplayName () : void {
$manager = $this -> getManager ();
2017-03-29 11:29:32 -04:00
$planetClosure = function ( $name ) {
2016-10-16 14:28:36 -04:00
return ucfirst ( $name );
};
2017-03-29 11:29:32 -04:00
$galaxyClosure = function ( $name ) {
2016-10-16 14:28:36 -04:00
return strtoupper ( $name );
};
$manager -> registerDisplayNameResolver ( 'planet' , $planetClosure );
$manager -> registerDisplayNameResolver ( 'galaxy' , $galaxyClosure );
$this -> assertSame ( 'Neptune' , $manager -> resolveDisplayName ( 'planet' , 'neptune' ));
$this -> assertSame ( 'SOMBRERO' , $manager -> resolveDisplayName ( 'galaxy' , 'sombrero' ));
}
2020-08-11 15:32:18 -04:00
2016-10-16 14:28:36 -04:00
public function testRegisterResolverDuplicate () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \OutOfBoundsException :: class );
2016-10-16 14:28:36 -04:00
$manager = $this -> getManager ();
2017-03-29 11:29:32 -04:00
$planetClosure = function ( $name ) {
2016-10-16 14:28:36 -04:00
return ucfirst ( $name );
};
$manager -> registerDisplayNameResolver ( 'planet' , $planetClosure );
$manager -> registerDisplayNameResolver ( 'planet' , $planetClosure );
}
2020-08-11 15:32:18 -04:00
2016-10-16 14:28:36 -04:00
public function testRegisterResolverInvalidType () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \InvalidArgumentException :: class );
2016-10-16 14:28:36 -04:00
$manager = $this -> getManager ();
2017-03-29 11:29:32 -04:00
$planetClosure = function ( $name ) {
2016-10-16 14:28:36 -04:00
return ucfirst ( $name );
};
$manager -> registerDisplayNameResolver ( 1337 , $planetClosure );
}
2020-08-11 15:32:18 -04:00
2016-10-16 14:28:36 -04:00
public function testResolveDisplayNameUnregisteredType () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \OutOfBoundsException :: class );
2016-10-16 14:28:36 -04:00
$manager = $this -> getManager ();
2017-03-29 11:29:32 -04:00
$planetClosure = function ( $name ) {
2016-10-16 14:28:36 -04:00
return ucfirst ( $name );
};
$manager -> registerDisplayNameResolver ( 'planet' , $planetClosure );
$manager -> resolveDisplayName ( 'galaxy' , 'sombrero' );
}
public function testResolveDisplayNameDirtyResolver () : void {
$manager = $this -> getManager ();
2017-03-29 11:29:32 -04:00
$planetClosure = function () {
return null ;
};
2016-10-16 14:28:36 -04:00
$manager -> registerDisplayNameResolver ( 'planet' , $planetClosure );
$this -> assertTrue ( is_string ( $manager -> resolveDisplayName ( 'planet' , 'neptune' )));
}
2022-01-12 11:31:38 -05:00
private function skipIfNotSupport4ByteUTF () {
2022-01-13 09:08:52 -05:00
if ( ! $this -> getManager () -> supportReactions ()) {
2022-01-12 11:31:38 -05:00
$this -> markTestSkipped ( 'MySQL doesn\'t support 4 byte UTF-8' );
}
}
2022-01-11 10:20:51 -05:00
/**
* @ dataProvider providerTestReactionAddAndDelete
*
2022-01-21 06:40:29 -05:00
* @ param IComment [] $comments
* @ param array $reactionsExpected
2022-01-11 10:20:51 -05:00
* @ return void
*/
public function testReactionAddAndDelete ( array $comments , array $reactionsExpected ) : void {
2022-01-12 11:31:38 -05:00
$this -> skipIfNotSupport4ByteUTF ();
2022-01-11 10:20:51 -05:00
$manager = $this -> getManager ();
2022-01-21 06:40:29 -05:00
$processedComments = $this -> proccessComments ( $comments );
$comment = end ( $processedComments );
2022-01-11 10:20:51 -05:00
if ( $comment -> getParentId ()) {
$parent = $manager -> get ( $comment -> getParentId ());
$this -> assertEqualsCanonicalizing ( $reactionsExpected , $parent -> getReactions ());
}
}
public function providerTestReactionAddAndDelete () : array {
return [
[
[
[ 'message' , 'alice' , 'comment' , null ],
], [],
],
[
[
[ 'message' , 'alice' , 'comment' , null ],
2022-01-21 06:40:29 -05:00
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
2022-01-11 10:20:51 -05:00
], [ '👍' => 1 ],
],
[
[
[ 'message' , 'alice' , 'comment' , null ],
2022-01-21 06:40:29 -05:00
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
2022-01-11 10:20:51 -05:00
], [ '👍' => 1 ],
],
[
[
[ 'message' , 'alice' , 'comment' , null ],
2022-01-21 06:40:29 -05:00
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
[ '👍' , 'frank' , 'reaction' , 'message#alice' ],
2022-01-11 10:20:51 -05:00
], [ '👍' => 2 ],
],
[
[
[ 'message' , 'alice' , 'comment' , null ],
2022-01-21 06:40:29 -05:00
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
[ '👍' , 'frank' , 'reaction' , 'message#alice' ],
[ '👍' , 'frank' , 'reaction_deleted' , 'message#alice' ],
2022-01-11 10:20:51 -05:00
], [ '👍' => 1 ],
],
[
[
[ 'message' , 'alice' , 'comment' , null ],
2022-01-21 06:40:29 -05:00
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
[ '👍' , 'frank' , 'reaction' , 'message#alice' ],
[ '👍' , 'alice' , 'reaction_deleted' , 'message#alice' ],
[ '👍' , 'frank' , 'reaction_deleted' , 'message#alice' ],
2022-01-11 10:20:51 -05:00
], [],
],
];
}
2020-08-11 15:32:18 -04:00
2016-10-16 14:28:36 -04:00
public function testResolveDisplayNameInvalidType () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \InvalidArgumentException :: class );
2016-10-16 14:28:36 -04:00
$manager = $this -> getManager ();
2017-03-29 11:29:32 -04:00
$planetClosure = function () {
return null ;
};
2016-10-16 14:28:36 -04:00
$manager -> registerDisplayNameResolver ( 'planet' , $planetClosure );
$this -> assertTrue ( is_string ( $manager -> resolveDisplayName ( 1337 , 'neptune' )));
}
2022-01-11 10:20:51 -05:00
/**
2022-01-21 06:40:29 -05:00
* @ param array $data
* @ return IComment []
2022-01-11 10:20:51 -05:00
*/
2022-01-21 06:40:29 -05:00
private function proccessComments ( array $data ) : array {
/** @var IComment[] */
$comments = [];
foreach ( $data as $comment ) {
[ $message , $actorId , $verb , $parentText ] = $comment ;
2022-01-11 10:20:51 -05:00
$parentId = null ;
if ( $parentText ) {
2022-01-21 06:40:29 -05:00
$parentId = ( string ) $comments [ $parentText ] -> getId ();
2022-01-11 10:20:51 -05:00
}
2022-01-21 06:40:29 -05:00
$id = '' ;
if ( $verb === 'reaction_deleted' ) {
$id = $comments [ $message . '#' . $actorId ] -> getId ();
2022-01-11 10:20:51 -05:00
}
2022-01-21 06:40:29 -05:00
$comment = $this -> testSave ( $message , $actorId , $verb , $parentId , $id );
$comments [ $comment -> getMessage () . '#' . $comment -> getActorId ()] = $comment ;
2022-01-11 10:20:51 -05:00
}
2022-01-21 06:40:29 -05:00
return $comments ;
}
/**
* @ dataProvider providerTestRetrieveAllReactions
*/
public function testRetrieveAllReactions ( array $comments , array $expected ) : void {
$this -> skipIfNotSupport4ByteUTF ();
$manager = $this -> getManager ();
$processedComments = $this -> proccessComments ( $comments );
$comment = reset ( $processedComments );
$all = $manager -> retrieveAllReactions ( $comment -> getId ());
2022-01-11 10:20:51 -05:00
$actual = array_map ( function ( $row ) {
return [
'message' => $row -> getMessage (),
'actorId' => $row -> getActorId (),
];
}, $all );
$this -> assertEqualsCanonicalizing ( $expected , $actual );
}
public function providerTestRetrieveAllReactions () : array {
return [
[
[
[ 'message' , 'alice' , 'comment' , null ],
],
[],
],
[
[
[ 'message' , 'alice' , 'comment' , null ],
2022-01-21 06:40:29 -05:00
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
[ '👍' , 'frank' , 'reaction' , 'message#alice' ],
2022-01-11 10:20:51 -05:00
],
[
[ '👍' , 'alice' ],
[ '👍' , 'frank' ],
],
],
[
[
[ 'message' , 'alice' , 'comment' , null ],
2022-01-21 06:40:29 -05:00
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
[ '👍' , 'frank' , 'reaction' , 'message#alice' ],
2022-01-11 10:20:51 -05:00
],
[
[ '👍' , 'alice' ],
[ '👍' , 'frank' ],
],
],
2023-03-01 11:01:25 -05:00
[ # 600 reactions to cover chunk size when retrieve comments of reactions.
[
[ 'message' , 'alice' , 'comment' , null ],
[ '😀' , 'alice' , 'reaction' , 'message#alice' ],
[ '😃' , 'alice' , 'reaction' , 'message#alice' ],
[ '😄' , 'alice' , 'reaction' , 'message#alice' ],
[ '😁' , 'alice' , 'reaction' , 'message#alice' ],
[ '😆' , 'alice' , 'reaction' , 'message#alice' ],
[ '😅' , 'alice' , 'reaction' , 'message#alice' ],
[ '😂' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤣' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥲' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥹' , 'alice' , 'reaction' , 'message#alice' ],
[ '☺️' , 'alice' , 'reaction' , 'message#alice' ],
[ '😊' , 'alice' , 'reaction' , 'message#alice' ],
[ '😇' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙂' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙃' , 'alice' , 'reaction' , 'message#alice' ],
[ '😉' , 'alice' , 'reaction' , 'message#alice' ],
[ '😌' , 'alice' , 'reaction' , 'message#alice' ],
[ '😍' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥰' , 'alice' , 'reaction' , 'message#alice' ],
[ '😘' , 'alice' , 'reaction' , 'message#alice' ],
[ '😗' , 'alice' , 'reaction' , 'message#alice' ],
[ '😙' , 'alice' , 'reaction' , 'message#alice' ],
[ '😚' , 'alice' , 'reaction' , 'message#alice' ],
[ '😋' , 'alice' , 'reaction' , 'message#alice' ],
[ '😛' , 'alice' , 'reaction' , 'message#alice' ],
[ '😝' , 'alice' , 'reaction' , 'message#alice' ],
[ '😜' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤪' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤨' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧐' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤓' , 'alice' , 'reaction' , 'message#alice' ],
[ '😎' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥸' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤩' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥳' , 'alice' , 'reaction' , 'message#alice' ],
[ '😏' , 'alice' , 'reaction' , 'message#alice' ],
[ '😒' , 'alice' , 'reaction' , 'message#alice' ],
[ '😞' , 'alice' , 'reaction' , 'message#alice' ],
[ '😔' , 'alice' , 'reaction' , 'message#alice' ],
[ '😟' , 'alice' , 'reaction' , 'message#alice' ],
[ '😕' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙁' , 'alice' , 'reaction' , 'message#alice' ],
[ '☹️' , 'alice' , 'reaction' , 'message#alice' ],
[ '😣' , 'alice' , 'reaction' , 'message#alice' ],
[ '😖' , 'alice' , 'reaction' , 'message#alice' ],
[ '😫' , 'alice' , 'reaction' , 'message#alice' ],
[ '😩' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥺' , 'alice' , 'reaction' , 'message#alice' ],
[ '😢' , 'alice' , 'reaction' , 'message#alice' ],
[ '😭' , 'alice' , 'reaction' , 'message#alice' ],
[ '😮💨' , 'alice' , 'reaction' , 'message#alice' ],
[ '😤' , 'alice' , 'reaction' , 'message#alice' ],
[ '😠' , 'alice' , 'reaction' , 'message#alice' ],
[ '😡' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤬' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤯' , 'alice' , 'reaction' , 'message#alice' ],
[ '😳' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥵' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥶' , 'alice' , 'reaction' , 'message#alice' ],
[ '😱' , 'alice' , 'reaction' , 'message#alice' ],
[ '😨' , 'alice' , 'reaction' , 'message#alice' ],
[ '😰' , 'alice' , 'reaction' , 'message#alice' ],
[ '😥' , 'alice' , 'reaction' , 'message#alice' ],
[ '😓' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫣' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤗' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫡' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤔' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫢' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤭' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤫' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤥' , 'alice' , 'reaction' , 'message#alice' ],
[ '😶' , 'alice' , 'reaction' , 'message#alice' ],
[ '😶🌫️' , 'alice' , 'reaction' , 'message#alice' ],
[ '😐' , 'alice' , 'reaction' , 'message#alice' ],
[ '😑' , 'alice' , 'reaction' , 'message#alice' ],
[ '😬' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫠' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙄' , 'alice' , 'reaction' , 'message#alice' ],
[ '😯' , 'alice' , 'reaction' , 'message#alice' ],
[ '😦' , 'alice' , 'reaction' , 'message#alice' ],
[ '😧' , 'alice' , 'reaction' , 'message#alice' ],
[ '😮' , 'alice' , 'reaction' , 'message#alice' ],
[ '😲' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥱' , 'alice' , 'reaction' , 'message#alice' ],
[ '😴' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤤' , 'alice' , 'reaction' , 'message#alice' ],
[ '😪' , 'alice' , 'reaction' , 'message#alice' ],
[ '😵' , 'alice' , 'reaction' , 'message#alice' ],
[ '😵💫' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫥' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤐' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥴' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤢' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤮' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤧' , 'alice' , 'reaction' , 'message#alice' ],
[ '😷' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤒' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤕' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤑' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤠' , 'alice' , 'reaction' , 'message#alice' ],
[ '😈' , 'alice' , 'reaction' , 'message#alice' ],
[ '👿' , 'alice' , 'reaction' , 'message#alice' ],
[ '👹' , 'alice' , 'reaction' , 'message#alice' ],
[ '👺' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤡' , 'alice' , 'reaction' , 'message#alice' ],
[ '💩' , 'alice' , 'reaction' , 'message#alice' ],
[ '👻' , 'alice' , 'reaction' , 'message#alice' ],
[ '💀' , 'alice' , 'reaction' , 'message#alice' ],
[ '☠️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👾' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤖' , 'alice' , 'reaction' , 'message#alice' ],
[ '🎃' , 'alice' , 'reaction' , 'message#alice' ],
[ '😺' , 'alice' , 'reaction' , 'message#alice' ],
[ '😸' , 'alice' , 'reaction' , 'message#alice' ],
[ '😹' , 'alice' , 'reaction' , 'message#alice' ],
[ '😻' , 'alice' , 'reaction' , 'message#alice' ],
[ '😼' , 'alice' , 'reaction' , 'message#alice' ],
[ '😽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙀' , 'alice' , 'reaction' , 'message#alice' ],
[ '😿' , 'alice' , 'reaction' , 'message#alice' ],
[ '😾' , 'alice' , 'reaction' , 'message#alice' ],
[ '👶' , 'alice' , 'reaction' , 'message#alice' ],
[ '👧' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧒' , 'alice' , 'reaction' , 'message#alice' ],
[ '👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🦱' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🦱' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🦱' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🦰' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🦰' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🦰' , 'alice' , 'reaction' , 'message#alice' ],
[ '👱♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👱' , 'alice' , 'reaction' , 'message#alice' ],
[ '👱♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🦳' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🦳' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🦳' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🦲' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🦲' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🦲' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧔♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧔' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧔♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👵' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧓' , 'alice' , 'reaction' , 'message#alice' ],
[ '👴' , 'alice' , 'reaction' , 'message#alice' ],
[ '👲' , 'alice' , 'reaction' , 'message#alice' ],
[ '👳♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👳' , 'alice' , 'reaction' , 'message#alice' ],
[ '👳♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧕' , 'alice' , 'reaction' , 'message#alice' ],
[ '👮♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👮' , 'alice' , 'reaction' , 'message#alice' ],
[ '👮♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👷♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👷' , 'alice' , 'reaction' , 'message#alice' ],
[ '👷♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💂♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💂' , 'alice' , 'reaction' , 'message#alice' ],
[ '💂♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🕵️♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🕵️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🕵️♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩⚕️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑⚕️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨⚕️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🌾' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🌾' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🌾' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🍳' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🍳' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🍳' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🎓' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🎓' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🎓' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🎤' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🎤' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🎤' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏫' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏫' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏫' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏭' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏭' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏭' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩💻' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑💻' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨💻' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩💼' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑💼' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨💼' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🔧' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🔧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🔧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🔬' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🔬' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🔬' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🎨' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🎨' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🎨' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🚒' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🚒' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🚒' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩✈️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑✈️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨✈️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🚀' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🚀' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🚀' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩⚖️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑⚖️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨⚖️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👰♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👰' , 'alice' , 'reaction' , 'message#alice' ],
[ '👰♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤵♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤵' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤵♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👸' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫅' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤴' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥷' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦸♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦸' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦸♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦹♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦹' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦹♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤶' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🎄' , 'alice' , 'reaction' , 'message#alice' ],
[ '🎅' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧙♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧙' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧙♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧝♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧝' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧝♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧛♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧛' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧛♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧟♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧟' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧟♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧞♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧞' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧞♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧜♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧜' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧜♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧚♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧚' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧚♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧌' , 'alice' , 'reaction' , 'message#alice' ],
[ '👼' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤰' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫄' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫃' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤱' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🍼' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🍼' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🍼' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙇♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙇' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙇♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💁♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💁' , 'alice' , 'reaction' , 'message#alice' ],
[ '💁♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙅♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙅' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙅♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙆♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙆' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙆♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙋♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙋' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙋♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧏♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧏' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧏♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤦♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤦' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤦♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤷♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤷' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤷♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙎♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙎' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙎♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙍♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙍' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙍♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💇♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💇' , 'alice' , 'reaction' , 'message#alice' ],
[ '💇♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💆♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💆' , 'alice' , 'reaction' , 'message#alice' ],
[ '💆♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧖♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧖' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧖♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💅' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤳' , 'alice' , 'reaction' , 'message#alice' ],
[ '💃' , 'alice' , 'reaction' , 'message#alice' ],
[ '🕺' , 'alice' , 'reaction' , 'message#alice' ],
[ '👯♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👯' , 'alice' , 'reaction' , 'message#alice' ],
[ '👯♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🕴' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🦽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🦽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🦽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🦼' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🦼' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🦼' , 'alice' , 'reaction' , 'message#alice' ],
[ '🚶♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🚶' , 'alice' , 'reaction' , 'message#alice' ],
[ '🚶♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🦯' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🦯' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🦯' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧎♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧎' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧎♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🏃♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🏃' , 'alice' , 'reaction' , 'message#alice' ],
[ '🏃♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧍♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧍' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧍♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👭' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🤝🧑' , 'alice' , 'reaction' , 'message#alice' ],
[ '👬' , 'alice' , 'reaction' , 'message#alice' ],
[ '👫' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩❤️👩' , 'alice' , 'reaction' , 'message#alice' ],
[ '💑' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨❤️👨' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩❤️👨' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩❤️💋👩' , 'alice' , 'reaction' , 'message#alice' ],
[ '💏' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨❤️💋👨' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩❤️💋👨' , 'alice' , 'reaction' , 'message#alice' ],
[ '👪' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👩👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👩👧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👩👧👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👩👦👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👩👧👧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👨👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👨👧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👨👧👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👨👦👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👨👧👧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩👩👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩👩👧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩👩👧👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩👩👦👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩👩👧👧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👦👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👧👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨👧👧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩👦👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩👧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩👧👦' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩👧👧' , 'alice' , 'reaction' , 'message#alice' ],
[ '🗣' , 'alice' , 'reaction' , 'message#alice' ],
[ '👤' , 'alice' , 'reaction' , 'message#alice' ],
[ '👥' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫂' , 'alice' , 'reaction' , 'message#alice' ],
[ '👋🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤚🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🖐🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '✋🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🖖🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👌🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤌🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤏🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '✌🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤞🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫰🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤟🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤘🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤙🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫵🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫱🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫲🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫳🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫴🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👈🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👉🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👆🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🖕🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👇🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '☝🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👍🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👎🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '✊🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👊🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤛🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤜🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👏🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫶🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙌🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👐🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤲🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙏🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '✍🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '💅🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤳🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '💪🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦵🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦶🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👂🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦻🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👃🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👶🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👧🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧒🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👦🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🦱' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🦱' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🦱' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🦰' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🦰' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🦰' , 'alice' , 'reaction' , 'message#alice' ],
[ '👱🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👱🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👱🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🦳' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🦳' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🦳' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🦲' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🦲' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🦲' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧔🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧔🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧔🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👵🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧓🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👴🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👲🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👳🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👳🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👳🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧕🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👮🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👮🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👮🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👷🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👷🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👷🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💂🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💂🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '💂🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🕵🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🕵🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🕵🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽⚕️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽⚕️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽⚕️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🌾' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🌾' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🌾' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🍳' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🍳' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🍳' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🎓' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🎓' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🎓' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🎤' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🎤' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🎤' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🏫' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🏫' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🏫' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🏭' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🏭' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🏭' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽💻' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽💻' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽💻' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽💼' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽💼' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽💼' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🔧' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🔧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🔧' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🔬' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🔬' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🔬' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🎨' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🎨' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🎨' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🚒' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🚒' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🚒' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽✈️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽✈️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽✈️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🚀' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🚀' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🚀' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽⚖️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽⚖️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽⚖️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👰🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👰🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👰🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤵🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤵🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤵🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👸🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫅🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤴🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🥷🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦸🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦸🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦸🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦹🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦹🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🦹🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤶🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🎄' , 'alice' , 'reaction' , 'message#alice' ],
[ '🎅🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧙🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧙🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧙🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧝🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧝🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧝🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧛🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧛🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧛🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧜🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧜🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧜🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧚🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧚🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧚🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '👼🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤰🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫄🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🫃🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤱🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🍼' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🍼' , 'alice' , 'reaction' , 'message#alice' ],
[ '👨🏽🍼' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙇🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙇🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙇🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💁🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💁🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '💁🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙅🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙅🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙅🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙆🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙆🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙆🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙋🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙋🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙋🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧏🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧏🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧏🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤦🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤦🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤦🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤷🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤷🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🤷🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙎🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙎🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙎🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙍🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙍🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🙍🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💇🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💇🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '💇🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💆🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💆🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '💆🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧖🏽♀️' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧖🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧖🏽♂️' , 'alice' , 'reaction' , 'message#alice' ],
[ '💃🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🕺🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🕴🏽' , 'alice' , 'reaction' , 'message#alice' ],
[ '👩🏽🦽' , 'alice' , 'reaction' , 'message#alice' ],
[ '🧑🏽🦽' , 'alice' , 'reaction' , 'message#alice' ],
],
[
[ '😀' , 'alice' ],
[ '😃' , 'alice' ],
[ '😄' , 'alice' ],
[ '😁' , 'alice' ],
[ '😆' , 'alice' ],
[ '😅' , 'alice' ],
[ '😂' , 'alice' ],
[ '🤣' , 'alice' ],
[ '🥲' , 'alice' ],
[ '🥹' , 'alice' ],
[ '☺️' , 'alice' ],
[ '😊' , 'alice' ],
[ '😇' , 'alice' ],
[ '🙂' , 'alice' ],
[ '🙃' , 'alice' ],
[ '😉' , 'alice' ],
[ '😌' , 'alice' ],
[ '😍' , 'alice' ],
[ '🥰' , 'alice' ],
[ '😘' , 'alice' ],
[ '😗' , 'alice' ],
[ '😙' , 'alice' ],
[ '😚' , 'alice' ],
[ '😋' , 'alice' ],
[ '😛' , 'alice' ],
[ '😝' , 'alice' ],
[ '😜' , 'alice' ],
[ '🤪' , 'alice' ],
[ '🤨' , 'alice' ],
[ '🧐' , 'alice' ],
[ '🤓' , 'alice' ],
[ '😎' , 'alice' ],
[ '🥸' , 'alice' ],
[ '🤩' , 'alice' ],
[ '🥳' , 'alice' ],
[ '😏' , 'alice' ],
[ '😒' , 'alice' ],
[ '😞' , 'alice' ],
[ '😔' , 'alice' ],
[ '😟' , 'alice' ],
[ '😕' , 'alice' ],
[ '🙁' , 'alice' ],
[ '☹️' , 'alice' ],
[ '😣' , 'alice' ],
[ '😖' , 'alice' ],
[ '😫' , 'alice' ],
[ '😩' , 'alice' ],
[ '🥺' , 'alice' ],
[ '😢' , 'alice' ],
[ '😭' , 'alice' ],
[ '😮💨' , 'alice' ],
[ '😤' , 'alice' ],
[ '😠' , 'alice' ],
[ '😡' , 'alice' ],
[ '🤬' , 'alice' ],
[ '🤯' , 'alice' ],
[ '😳' , 'alice' ],
[ '🥵' , 'alice' ],
[ '🥶' , 'alice' ],
[ '😱' , 'alice' ],
[ '😨' , 'alice' ],
[ '😰' , 'alice' ],
[ '😥' , 'alice' ],
[ '😓' , 'alice' ],
[ '🫣' , 'alice' ],
[ '🤗' , 'alice' ],
[ '🫡' , 'alice' ],
[ '🤔' , 'alice' ],
[ '🫢' , 'alice' ],
[ '🤭' , 'alice' ],
[ '🤫' , 'alice' ],
[ '🤥' , 'alice' ],
[ '😶' , 'alice' ],
[ '😶🌫️' , 'alice' ],
[ '😐' , 'alice' ],
[ '😑' , 'alice' ],
[ '😬' , 'alice' ],
[ '🫠' , 'alice' ],
[ '🙄' , 'alice' ],
[ '😯' , 'alice' ],
[ '😦' , 'alice' ],
[ '😧' , 'alice' ],
[ '😮' , 'alice' ],
[ '😲' , 'alice' ],
[ '🥱' , 'alice' ],
[ '😴' , 'alice' ],
[ '🤤' , 'alice' ],
[ '😪' , 'alice' ],
[ '😵' , 'alice' ],
[ '😵💫' , 'alice' ],
[ '🫥' , 'alice' ],
[ '🤐' , 'alice' ],
[ '🥴' , 'alice' ],
[ '🤢' , 'alice' ],
[ '🤮' , 'alice' ],
[ '🤧' , 'alice' ],
[ '😷' , 'alice' ],
[ '🤒' , 'alice' ],
[ '🤕' , 'alice' ],
[ '🤑' , 'alice' ],
[ '🤠' , 'alice' ],
[ '😈' , 'alice' ],
[ '👿' , 'alice' ],
[ '👹' , 'alice' ],
[ '👺' , 'alice' ],
[ '🤡' , 'alice' ],
[ '💩' , 'alice' ],
[ '👻' , 'alice' ],
[ '💀' , 'alice' ],
[ '☠️' , 'alice' ],
[ '👽' , 'alice' ],
[ '👾' , 'alice' ],
[ '🤖' , 'alice' ],
[ '🎃' , 'alice' ],
[ '😺' , 'alice' ],
[ '😸' , 'alice' ],
[ '😹' , 'alice' ],
[ '😻' , 'alice' ],
[ '😼' , 'alice' ],
[ '😽' , 'alice' ],
[ '🙀' , 'alice' ],
[ '😿' , 'alice' ],
[ '😾' , 'alice' ],
[ '👶' , 'alice' ],
[ '👧' , 'alice' ],
[ '🧒' , 'alice' ],
[ '👦' , 'alice' ],
[ '👩' , 'alice' ],
[ '🧑' , 'alice' ],
[ '👨' , 'alice' ],
[ '👩🦱' , 'alice' ],
[ '🧑🦱' , 'alice' ],
[ '👨🦱' , 'alice' ],
[ '👩🦰' , 'alice' ],
[ '🧑🦰' , 'alice' ],
[ '👨🦰' , 'alice' ],
[ '👱♀️' , 'alice' ],
[ '👱' , 'alice' ],
[ '👱♂️' , 'alice' ],
[ '👩🦳' , 'alice' ],
[ '🧑🦳' , 'alice' ],
[ '👨🦳' , 'alice' ],
[ '👩🦲' , 'alice' ],
[ '🧑🦲' , 'alice' ],
[ '👨🦲' , 'alice' ],
[ '🧔♀️' , 'alice' ],
[ '🧔' , 'alice' ],
[ '🧔♂️' , 'alice' ],
[ '👵' , 'alice' ],
[ '🧓' , 'alice' ],
[ '👴' , 'alice' ],
[ '👲' , 'alice' ],
[ '👳♀️' , 'alice' ],
[ '👳' , 'alice' ],
[ '👳♂️' , 'alice' ],
[ '🧕' , 'alice' ],
[ '👮♀️' , 'alice' ],
[ '👮' , 'alice' ],
[ '👮♂️' , 'alice' ],
[ '👷♀️' , 'alice' ],
[ '👷' , 'alice' ],
[ '👷♂️' , 'alice' ],
[ '💂♀️' , 'alice' ],
[ '💂' , 'alice' ],
[ '💂♂️' , 'alice' ],
[ '🕵️♀️' , 'alice' ],
[ '🕵️' , 'alice' ],
[ '🕵️♂️' , 'alice' ],
[ '👩⚕️' , 'alice' ],
[ '🧑⚕️' , 'alice' ],
[ '👨⚕️' , 'alice' ],
[ '👩🌾' , 'alice' ],
[ '🧑🌾' , 'alice' ],
[ '👨🌾' , 'alice' ],
[ '👩🍳' , 'alice' ],
[ '🧑🍳' , 'alice' ],
[ '👨🍳' , 'alice' ],
[ '👩🎓' , 'alice' ],
[ '🧑🎓' , 'alice' ],
[ '👨🎓' , 'alice' ],
[ '👩🎤' , 'alice' ],
[ '🧑🎤' , 'alice' ],
[ '👨🎤' , 'alice' ],
[ '👩🏫' , 'alice' ],
[ '🧑🏫' , 'alice' ],
[ '👨🏫' , 'alice' ],
[ '👩🏭' , 'alice' ],
[ '🧑🏭' , 'alice' ],
[ '👨🏭' , 'alice' ],
[ '👩💻' , 'alice' ],
[ '🧑💻' , 'alice' ],
[ '👨💻' , 'alice' ],
[ '👩💼' , 'alice' ],
[ '🧑💼' , 'alice' ],
[ '👨💼' , 'alice' ],
[ '👩🔧' , 'alice' ],
[ '🧑🔧' , 'alice' ],
[ '👨🔧' , 'alice' ],
[ '👩🔬' , 'alice' ],
[ '🧑🔬' , 'alice' ],
[ '👨🔬' , 'alice' ],
[ '👩🎨' , 'alice' ],
[ '🧑🎨' , 'alice' ],
[ '👨🎨' , 'alice' ],
[ '👩🚒' , 'alice' ],
[ '🧑🚒' , 'alice' ],
[ '👨🚒' , 'alice' ],
[ '👩✈️' , 'alice' ],
[ '🧑✈️' , 'alice' ],
[ '👨✈️' , 'alice' ],
[ '👩🚀' , 'alice' ],
[ '🧑🚀' , 'alice' ],
[ '👨🚀' , 'alice' ],
[ '👩⚖️' , 'alice' ],
[ '🧑⚖️' , 'alice' ],
[ '👨⚖️' , 'alice' ],
[ '👰♀️' , 'alice' ],
[ '👰' , 'alice' ],
[ '👰♂️' , 'alice' ],
[ '🤵♀️' , 'alice' ],
[ '🤵' , 'alice' ],
[ '🤵♂️' , 'alice' ],
[ '👸' , 'alice' ],
[ '🫅' , 'alice' ],
[ '🤴' , 'alice' ],
[ '🥷' , 'alice' ],
[ '🦸♀️' , 'alice' ],
[ '🦸' , 'alice' ],
[ '🦸♂️' , 'alice' ],
[ '🦹♀️' , 'alice' ],
[ '🦹' , 'alice' ],
[ '🦹♂️' , 'alice' ],
[ '🤶' , 'alice' ],
[ '🧑🎄' , 'alice' ],
[ '🎅' , 'alice' ],
[ '🧙♀️' , 'alice' ],
[ '🧙' , 'alice' ],
[ '🧙♂️' , 'alice' ],
[ '🧝♀️' , 'alice' ],
[ '🧝' , 'alice' ],
[ '🧝♂️' , 'alice' ],
[ '🧛♀️' , 'alice' ],
[ '🧛' , 'alice' ],
[ '🧛♂️' , 'alice' ],
[ '🧟♀️' , 'alice' ],
[ '🧟' , 'alice' ],
[ '🧟♂️' , 'alice' ],
[ '🧞♀️' , 'alice' ],
[ '🧞' , 'alice' ],
[ '🧞♂️' , 'alice' ],
[ '🧜♀️' , 'alice' ],
[ '🧜' , 'alice' ],
[ '🧜♂️' , 'alice' ],
[ '🧚♀️' , 'alice' ],
[ '🧚' , 'alice' ],
[ '🧚♂️' , 'alice' ],
[ '🧌' , 'alice' ],
[ '👼' , 'alice' ],
[ '🤰' , 'alice' ],
[ '🫄' , 'alice' ],
[ '🫃' , 'alice' ],
[ '🤱' , 'alice' ],
[ '👩🍼' , 'alice' ],
[ '🧑🍼' , 'alice' ],
[ '👨🍼' , 'alice' ],
[ '🙇♀️' , 'alice' ],
[ '🙇' , 'alice' ],
[ '🙇♂️' , 'alice' ],
[ '💁♀️' , 'alice' ],
[ '💁' , 'alice' ],
[ '💁♂️' , 'alice' ],
[ '🙅♀️' , 'alice' ],
[ '🙅' , 'alice' ],
[ '🙅♂️' , 'alice' ],
[ '🙆♀️' , 'alice' ],
[ '🙆' , 'alice' ],
[ '🙆♂️' , 'alice' ],
[ '🙋♀️' , 'alice' ],
[ '🙋' , 'alice' ],
[ '🙋♂️' , 'alice' ],
[ '🧏♀️' , 'alice' ],
[ '🧏' , 'alice' ],
[ '🧏♂️' , 'alice' ],
[ '🤦♀️' , 'alice' ],
[ '🤦' , 'alice' ],
[ '🤦♂️' , 'alice' ],
[ '🤷♀️' , 'alice' ],
[ '🤷' , 'alice' ],
[ '🤷♂️' , 'alice' ],
[ '🙎♀️' , 'alice' ],
[ '🙎' , 'alice' ],
[ '🙎♂️' , 'alice' ],
[ '🙍♀️' , 'alice' ],
[ '🙍' , 'alice' ],
[ '🙍♂️' , 'alice' ],
[ '💇♀️' , 'alice' ],
[ '💇' , 'alice' ],
[ '💇♂️' , 'alice' ],
[ '💆♀️' , 'alice' ],
[ '💆' , 'alice' ],
[ '💆♂️' , 'alice' ],
[ '🧖♀️' , 'alice' ],
[ '🧖' , 'alice' ],
[ '🧖♂️' , 'alice' ],
[ '💅' , 'alice' ],
[ '🤳' , 'alice' ],
[ '💃' , 'alice' ],
[ '🕺' , 'alice' ],
[ '👯♀️' , 'alice' ],
[ '👯' , 'alice' ],
[ '👯♂️' , 'alice' ],
[ '🕴' , 'alice' ],
[ '👩🦽' , 'alice' ],
[ '🧑🦽' , 'alice' ],
[ '👨🦽' , 'alice' ],
[ '👩🦼' , 'alice' ],
[ '🧑🦼' , 'alice' ],
[ '👨🦼' , 'alice' ],
[ '🚶♀️' , 'alice' ],
[ '🚶' , 'alice' ],
[ '🚶♂️' , 'alice' ],
[ '👩🦯' , 'alice' ],
[ '🧑🦯' , 'alice' ],
[ '👨🦯' , 'alice' ],
[ '🧎♀️' , 'alice' ],
[ '🧎' , 'alice' ],
[ '🧎♂️' , 'alice' ],
[ '🏃♀️' , 'alice' ],
[ '🏃' , 'alice' ],
[ '🏃♂️' , 'alice' ],
[ '🧍♀️' , 'alice' ],
[ '🧍' , 'alice' ],
[ '🧍♂️' , 'alice' ],
[ '👭' , 'alice' ],
[ '🧑🤝🧑' , 'alice' ],
[ '👬' , 'alice' ],
[ '👫' , 'alice' ],
[ '👩❤️👩' , 'alice' ],
[ '💑' , 'alice' ],
[ '👨❤️👨' , 'alice' ],
[ '👩❤️👨' , 'alice' ],
[ '👩❤️💋👩' , 'alice' ],
[ '💏' , 'alice' ],
[ '👨❤️💋👨' , 'alice' ],
[ '👩❤️💋👨' , 'alice' ],
[ '👪' , 'alice' ],
[ '👨👩👦' , 'alice' ],
[ '👨👩👧' , 'alice' ],
[ '👨👩👧👦' , 'alice' ],
[ '👨👩👦👦' , 'alice' ],
[ '👨👩👧👧' , 'alice' ],
[ '👨👨👦' , 'alice' ],
[ '👨👨👧' , 'alice' ],
[ '👨👨👧👦' , 'alice' ],
[ '👨👨👦👦' , 'alice' ],
[ '👨👨👧👧' , 'alice' ],
[ '👩👩👦' , 'alice' ],
[ '👩👩👧' , 'alice' ],
[ '👩👩👧👦' , 'alice' ],
[ '👩👩👦👦' , 'alice' ],
[ '👩👩👧👧' , 'alice' ],
[ '👨👦' , 'alice' ],
[ '👨👦👦' , 'alice' ],
[ '👨👧' , 'alice' ],
[ '👨👧👦' , 'alice' ],
[ '👨👧👧' , 'alice' ],
[ '👩👦' , 'alice' ],
[ '👩👦👦' , 'alice' ],
[ '👩👧' , 'alice' ],
[ '👩👧👦' , 'alice' ],
[ '👩👧👧' , 'alice' ],
[ '🗣' , 'alice' ],
[ '👤' , 'alice' ],
[ '👥' , 'alice' ],
[ '🫂' , 'alice' ],
[ '👋🏽' , 'alice' ],
[ '🤚🏽' , 'alice' ],
[ '🖐🏽' , 'alice' ],
[ '✋🏽' , 'alice' ],
[ '🖖🏽' , 'alice' ],
[ '👌🏽' , 'alice' ],
[ '🤌🏽' , 'alice' ],
[ '🤏🏽' , 'alice' ],
[ '✌🏽' , 'alice' ],
[ '🤞🏽' , 'alice' ],
[ '🫰🏽' , 'alice' ],
[ '🤟🏽' , 'alice' ],
[ '🤘🏽' , 'alice' ],
[ '🤙🏽' , 'alice' ],
[ '🫵🏽' , 'alice' ],
[ '🫱🏽' , 'alice' ],
[ '🫲🏽' , 'alice' ],
[ '🫳🏽' , 'alice' ],
[ '🫴🏽' , 'alice' ],
[ '👈🏽' , 'alice' ],
[ '👉🏽' , 'alice' ],
[ '👆🏽' , 'alice' ],
[ '🖕🏽' , 'alice' ],
[ '👇🏽' , 'alice' ],
[ '☝🏽' , 'alice' ],
[ '👍🏽' , 'alice' ],
[ '👎🏽' , 'alice' ],
[ '✊🏽' , 'alice' ],
[ '👊🏽' , 'alice' ],
[ '🤛🏽' , 'alice' ],
[ '🤜🏽' , 'alice' ],
[ '👏🏽' , 'alice' ],
[ '🫶🏽' , 'alice' ],
[ '🙌🏽' , 'alice' ],
[ '👐🏽' , 'alice' ],
[ '🤲🏽' , 'alice' ],
[ '🙏🏽' , 'alice' ],
[ '✍🏽' , 'alice' ],
[ '💅🏽' , 'alice' ],
[ '🤳🏽' , 'alice' ],
[ '💪🏽' , 'alice' ],
[ '🦵🏽' , 'alice' ],
[ '🦶🏽' , 'alice' ],
[ '👂🏽' , 'alice' ],
[ '🦻🏽' , 'alice' ],
[ '👃🏽' , 'alice' ],
[ '👶🏽' , 'alice' ],
[ '👧🏽' , 'alice' ],
[ '🧒🏽' , 'alice' ],
[ '👦🏽' , 'alice' ],
[ '👩🏽' , 'alice' ],
[ '🧑🏽' , 'alice' ],
[ '👨🏽' , 'alice' ],
[ '👩🏽🦱' , 'alice' ],
[ '🧑🏽🦱' , 'alice' ],
[ '👨🏽🦱' , 'alice' ],
[ '👩🏽🦰' , 'alice' ],
[ '🧑🏽🦰' , 'alice' ],
[ '👨🏽🦰' , 'alice' ],
[ '👱🏽♀️' , 'alice' ],
[ '👱🏽' , 'alice' ],
[ '👱🏽♂️' , 'alice' ],
[ '👩🏽🦳' , 'alice' ],
[ '🧑🏽🦳' , 'alice' ],
[ '👨🏽🦳' , 'alice' ],
[ '👩🏽🦲' , 'alice' ],
[ '🧑🏽🦲' , 'alice' ],
[ '👨🏽🦲' , 'alice' ],
[ '🧔🏽♀️' , 'alice' ],
[ '🧔🏽' , 'alice' ],
[ '🧔🏽♂️' , 'alice' ],
[ '👵🏽' , 'alice' ],
[ '🧓🏽' , 'alice' ],
[ '👴🏽' , 'alice' ],
[ '👲🏽' , 'alice' ],
[ '👳🏽♀️' , 'alice' ],
[ '👳🏽' , 'alice' ],
[ '👳🏽♂️' , 'alice' ],
[ '🧕🏽' , 'alice' ],
[ '👮🏽♀️' , 'alice' ],
[ '👮🏽' , 'alice' ],
[ '👮🏽♂️' , 'alice' ],
[ '👷🏽♀️' , 'alice' ],
[ '👷🏽' , 'alice' ],
[ '👷🏽♂️' , 'alice' ],
[ '💂🏽♀️' , 'alice' ],
[ '💂🏽' , 'alice' ],
[ '💂🏽♂️' , 'alice' ],
[ '🕵🏽♀️' , 'alice' ],
[ '🕵🏽' , 'alice' ],
[ '🕵🏽♂️' , 'alice' ],
[ '👩🏽⚕️' , 'alice' ],
[ '🧑🏽⚕️' , 'alice' ],
[ '👨🏽⚕️' , 'alice' ],
[ '👩🏽🌾' , 'alice' ],
[ '🧑🏽🌾' , 'alice' ],
[ '👨🏽🌾' , 'alice' ],
[ '👩🏽🍳' , 'alice' ],
[ '🧑🏽🍳' , 'alice' ],
[ '👨🏽🍳' , 'alice' ],
[ '👩🏽🎓' , 'alice' ],
[ '🧑🏽🎓' , 'alice' ],
[ '👨🏽🎓' , 'alice' ],
[ '👩🏽🎤' , 'alice' ],
[ '🧑🏽🎤' , 'alice' ],
[ '👨🏽🎤' , 'alice' ],
[ '👩🏽🏫' , 'alice' ],
[ '🧑🏽🏫' , 'alice' ],
[ '👨🏽🏫' , 'alice' ],
[ '👩🏽🏭' , 'alice' ],
[ '🧑🏽🏭' , 'alice' ],
[ '👨🏽🏭' , 'alice' ],
[ '👩🏽💻' , 'alice' ],
[ '🧑🏽💻' , 'alice' ],
[ '👨🏽💻' , 'alice' ],
[ '👩🏽💼' , 'alice' ],
[ '🧑🏽💼' , 'alice' ],
[ '👨🏽💼' , 'alice' ],
[ '👩🏽🔧' , 'alice' ],
[ '🧑🏽🔧' , 'alice' ],
[ '👨🏽🔧' , 'alice' ],
[ '👩🏽🔬' , 'alice' ],
[ '🧑🏽🔬' , 'alice' ],
[ '👨🏽🔬' , 'alice' ],
[ '👩🏽🎨' , 'alice' ],
[ '🧑🏽🎨' , 'alice' ],
[ '👨🏽🎨' , 'alice' ],
[ '👩🏽🚒' , 'alice' ],
[ '🧑🏽🚒' , 'alice' ],
[ '👨🏽🚒' , 'alice' ],
[ '👩🏽✈️' , 'alice' ],
[ '🧑🏽✈️' , 'alice' ],
[ '👨🏽✈️' , 'alice' ],
[ '👩🏽🚀' , 'alice' ],
[ '🧑🏽🚀' , 'alice' ],
[ '👨🏽🚀' , 'alice' ],
[ '👩🏽⚖️' , 'alice' ],
[ '🧑🏽⚖️' , 'alice' ],
[ '👨🏽⚖️' , 'alice' ],
[ '👰🏽♀️' , 'alice' ],
[ '👰🏽' , 'alice' ],
[ '👰🏽♂️' , 'alice' ],
[ '🤵🏽♀️' , 'alice' ],
[ '🤵🏽' , 'alice' ],
[ '🤵🏽♂️' , 'alice' ],
[ '👸🏽' , 'alice' ],
[ '🫅🏽' , 'alice' ],
[ '🤴🏽' , 'alice' ],
[ '🥷🏽' , 'alice' ],
[ '🦸🏽♀️' , 'alice' ],
[ '🦸🏽' , 'alice' ],
[ '🦸🏽♂️' , 'alice' ],
[ '🦹🏽♀️' , 'alice' ],
[ '🦹🏽' , 'alice' ],
[ '🦹🏽♂️' , 'alice' ],
[ '🤶🏽' , 'alice' ],
[ '🧑🏽🎄' , 'alice' ],
[ '🎅🏽' , 'alice' ],
[ '🧙🏽♀️' , 'alice' ],
[ '🧙🏽' , 'alice' ],
[ '🧙🏽♂️' , 'alice' ],
[ '🧝🏽♀️' , 'alice' ],
[ '🧝🏽' , 'alice' ],
[ '🧝🏽♂️' , 'alice' ],
[ '🧛🏽♀️' , 'alice' ],
[ '🧛🏽' , 'alice' ],
[ '🧛🏽♂️' , 'alice' ],
[ '🧜🏽♀️' , 'alice' ],
[ '🧜🏽' , 'alice' ],
[ '🧜🏽♂️' , 'alice' ],
[ '🧚🏽♀️' , 'alice' ],
[ '🧚🏽' , 'alice' ],
[ '🧚🏽♂️' , 'alice' ],
[ '👼🏽' , 'alice' ],
[ '🤰🏽' , 'alice' ],
[ '🫄🏽' , 'alice' ],
[ '🫃🏽' , 'alice' ],
[ '🤱🏽' , 'alice' ],
[ '👩🏽🍼' , 'alice' ],
[ '🧑🏽🍼' , 'alice' ],
[ '👨🏽🍼' , 'alice' ],
[ '🙇🏽♀️' , 'alice' ],
[ '🙇🏽' , 'alice' ],
[ '🙇🏽♂️' , 'alice' ],
[ '💁🏽♀️' , 'alice' ],
[ '💁🏽' , 'alice' ],
[ '💁🏽♂️' , 'alice' ],
[ '🙅🏽♀️' , 'alice' ],
[ '🙅🏽' , 'alice' ],
[ '🙅🏽♂️' , 'alice' ],
[ '🙆🏽♀️' , 'alice' ],
[ '🙆🏽' , 'alice' ],
[ '🙆🏽♂️' , 'alice' ],
[ '🙋🏽♀️' , 'alice' ],
[ '🙋🏽' , 'alice' ],
[ '🙋🏽♂️' , 'alice' ],
[ '🧏🏽♀️' , 'alice' ],
[ '🧏🏽' , 'alice' ],
[ '🧏🏽♂️' , 'alice' ],
[ '🤦🏽♀️' , 'alice' ],
[ '🤦🏽' , 'alice' ],
[ '🤦🏽♂️' , 'alice' ],
[ '🤷🏽♀️' , 'alice' ],
[ '🤷🏽' , 'alice' ],
[ '🤷🏽♂️' , 'alice' ],
[ '🙎🏽♀️' , 'alice' ],
[ '🙎🏽' , 'alice' ],
[ '🙎🏽♂️' , 'alice' ],
[ '🙍🏽♀️' , 'alice' ],
[ '🙍🏽' , 'alice' ],
[ '🙍🏽♂️' , 'alice' ],
[ '💇🏽♀️' , 'alice' ],
[ '💇🏽' , 'alice' ],
[ '💇🏽♂️' , 'alice' ],
[ '💆🏽♀️' , 'alice' ],
[ '💆🏽' , 'alice' ],
[ '💆🏽♂️' , 'alice' ],
[ '🧖🏽♀️' , 'alice' ],
[ '🧖🏽' , 'alice' ],
[ '🧖🏽♂️' , 'alice' ],
[ '💃🏽' , 'alice' ],
[ '🕺🏽' , 'alice' ],
[ '🕴🏽' , 'alice' ],
[ '👩🏽🦽' , 'alice' ],
[ '🧑🏽🦽' , 'alice' ],
],
],
2022-01-11 10:20:51 -05:00
];
}
/**
* @ dataProvider providerTestRetrieveAllReactionsWithSpecificReaction
*/
public function testRetrieveAllReactionsWithSpecificReaction ( array $comments , string $reaction , array $expected ) : void {
2022-01-12 11:31:38 -05:00
$this -> skipIfNotSupport4ByteUTF ();
2022-01-11 10:20:51 -05:00
$manager = $this -> getManager ();
2022-01-21 06:40:29 -05:00
$processedComments = $this -> proccessComments ( $comments );
$comment = reset ( $processedComments );
$all = $manager -> retrieveAllReactionsWithSpecificReaction ( $comment -> getId (), $reaction );
2022-01-11 10:20:51 -05:00
$actual = array_map ( function ( $row ) {
return [
'message' => $row -> getMessage (),
'actorId' => $row -> getActorId (),
];
}, $all );
$this -> assertEqualsCanonicalizing ( $expected , $actual );
}
public function providerTestRetrieveAllReactionsWithSpecificReaction () : array {
return [
[
[
[ 'message' , 'alice' , 'comment' , null ],
],
'👎' ,
[],
],
[
[
[ 'message' , 'alice' , 'comment' , null ],
2022-01-21 06:40:29 -05:00
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
[ '👍' , 'frank' , 'reaction' , 'message#alice' ],
2022-01-11 10:20:51 -05:00
],
'👍' ,
[
[ '👍' , 'alice' ],
[ '👍' , 'frank' ],
],
],
[
[
[ 'message' , 'alice' , 'comment' , null ],
2022-01-21 06:40:29 -05:00
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
[ '👎' , 'alice' , 'reaction' , 'message#alice' ],
[ '👍' , 'frank' , 'reaction' , 'message#alice' ],
2022-01-11 10:20:51 -05:00
],
'👎' ,
[
[ '👎' , 'alice' ],
],
],
];
}
/**
* @ dataProvider providerTestGetReactionComment
*/
2022-01-21 06:40:29 -05:00
public function testGetReactionComment ( array $comments , array $expected , bool $notFound ) : void {
2022-01-12 11:31:38 -05:00
$this -> skipIfNotSupport4ByteUTF ();
2022-01-11 10:20:51 -05:00
$manager = $this -> getManager ();
2022-01-21 06:40:29 -05:00
$processedComments = $this -> proccessComments ( $comments );
$keys = [ 'message' , 'actorId' , 'verb' , 'parent' ];
$expected = array_combine ( $keys , $expected );
if ( $notFound ) {
$this -> expectException ( \OCP\Comments\NotFoundException :: class );
2022-01-11 10:20:51 -05:00
}
2022-01-21 06:40:29 -05:00
$comment = $processedComments [ $expected [ 'message' ] . '#' . $expected [ 'actorId' ]];
2022-01-11 10:20:51 -05:00
$actual = $manager -> getReactionComment ( $comment -> getParentId (), $comment -> getActorType (), $comment -> getActorId (), $comment -> getMessage ());
2022-01-21 06:40:29 -05:00
if ( ! $notFound ) {
$this -> assertEquals ( $expected [ 'message' ], $actual -> getMessage ());
$this -> assertEquals ( $expected [ 'actorId' ], $actual -> getActorId ());
$this -> assertEquals ( $expected [ 'verb' ], $actual -> getVerb ());
$this -> assertEquals ( $processedComments [ $expected [ 'parent' ]] -> getId (), $actual -> getParentId ());
}
2022-01-11 10:20:51 -05:00
}
public function providerTestGetReactionComment () : array {
return [
[
[
2022-01-21 06:40:29 -05:00
[ 'message' , 'Matthew' , 'comment' , null ],
[ '👍' , 'Matthew' , 'reaction' , 'message#Matthew' ],
[ '👍' , 'Mark' , 'reaction' , 'message#Matthew' ],
[ '👍' , 'Luke' , 'reaction' , 'message#Matthew' ],
[ '👍' , 'John' , 'reaction' , 'message#Matthew' ],
2022-01-11 10:20:51 -05:00
],
2022-01-21 06:40:29 -05:00
[ '👍' , 'Matthew' , 'reaction' , 'message#Matthew' ],
false ,
2022-01-11 10:20:51 -05:00
],
[
[
2022-01-21 06:40:29 -05:00
[ 'message' , 'Matthew' , 'comment' , null ],
[ '👍' , 'Matthew' , 'reaction' , 'message#Matthew' ],
[ '👍' , 'Mark' , 'reaction' , 'message#Matthew' ],
[ '👍' , 'Luke' , 'reaction' , 'message#Matthew' ],
[ '👍' , 'John' , 'reaction' , 'message#Matthew' ],
2022-01-11 10:20:51 -05:00
],
2022-01-21 06:40:29 -05:00
[ '👍' , 'Mark' , 'reaction' , 'message#Matthew' ],
false ,
],
[
[
[ 'message' , 'Matthew' , 'comment' , null ],
[ '👎' , 'Matthew' , 'reaction' , 'message#Matthew' ],
],
[ '👎' , 'Matthew' , 'reaction' , 'message#Matthew' ],
false ,
],
[
[
[ 'message' , 'Matthew' , 'comment' , null ],
[ '👎' , 'Matthew' , 'reaction' , 'message#Matthew' ],
[ '👎' , 'Matthew' , 'reaction_deleted' , 'message#Matthew' ],
],
[ '👎' , 'Matthew' , 'reaction' , 'message#Matthew' ],
true ,
2022-01-11 10:20:51 -05:00
],
];
}
2022-01-14 09:14:52 -05:00
/**
* @ dataProvider providerTestReactionMessageSize
*/
public function testReactionMessageSize ( $reactionString , $valid ) : void {
2022-01-17 07:14:56 -05:00
$this -> skipIfNotSupport4ByteUTF ();
2022-01-14 09:14:52 -05:00
if ( ! $valid ) {
$this -> expectException ( \UnexpectedValueException :: class );
}
$manager = $this -> getManager ();
$comment = new Comment ();
$comment -> setMessage ( $reactionString )
-> setVerb ( 'reaction' )
-> setActor ( 'users' , 'alice' )
-> setObject ( 'files' , 'file64' );
$status = $manager -> save ( $comment );
$this -> assertTrue ( $status );
}
public function providerTestReactionMessageSize () : array {
return [
2022-03-24 10:13:09 -04:00
[ 'a' , false ],
[ '1' , false ],
2022-01-14 09:14:52 -05:00
[ '👍' , true ],
2022-03-24 10:13:09 -04:00
[ '👍👍' , false ],
2022-01-14 09:14:52 -05:00
[ '👍🏽' , true ],
2022-03-24 10:13:09 -04:00
[ '👨🏽💻' , true ],
[ '👨🏽💻👍' , false ],
2022-01-14 09:14:52 -05:00
];
}
/**
* @ dataProvider providerTestReactionsSummarizeOrdered
*/
2022-01-17 05:56:24 -05:00
public function testReactionsSummarizeOrdered ( array $comments , array $expected , bool $isFullMatch ) : void {
2022-01-14 09:14:52 -05:00
$this -> skipIfNotSupport4ByteUTF ();
$manager = $this -> getManager ();
2022-01-21 06:40:29 -05:00
$processedComments = $this -> proccessComments ( $comments );
$comment = end ( $processedComments );
2022-01-14 09:14:52 -05:00
$actual = $manager -> get ( $comment -> getParentId ());
2022-01-17 05:56:24 -05:00
if ( $isFullMatch ) {
$this -> assertSame ( $expected , $actual -> getReactions ());
} else {
$subResult = array_slice ( $actual -> getReactions (), 0 , count ( $expected ));
$this -> assertSame ( $expected , $subResult );
}
2022-01-14 09:14:52 -05:00
}
public function providerTestReactionsSummarizeOrdered () : array {
return [
[
[
[ 'message' , 'alice' , 'comment' , null ],
2022-01-21 06:40:29 -05:00
[ '👍' , 'alice' , 'reaction' , 'message#alice' ],
2022-01-14 09:14:52 -05:00
],
[ '👍' => 1 ],
2022-01-17 05:56:24 -05:00
true ,
2022-01-14 09:14:52 -05:00
],
[
[
[ 'message' , 'alice' , 'comment' , null ],
2022-01-21 06:40:29 -05:00
[ '👎' , 'John' , 'reaction' , 'message#alice' ],
[ '💼' , 'Luke' , 'reaction' , 'message#alice' ],
[ '📋' , 'Luke' , 'reaction' , 'message#alice' ],
[ '🚀' , 'Luke' , 'reaction' , 'message#alice' ],
[ '🖤' , 'Luke' , 'reaction' , 'message#alice' ],
[ '😜' , 'Luke' , 'reaction' , 'message#alice' ],
[ '🌖' , 'Luke' , 'reaction' , 'message#alice' ],
[ '💖' , 'Luke' , 'reaction' , 'message#alice' ],
[ '📥' , 'Luke' , 'reaction' , 'message#alice' ],
[ '🐉' , 'Luke' , 'reaction' , 'message#alice' ],
[ '☕' , 'Luke' , 'reaction' , 'message#alice' ],
[ '🐄' , 'Luke' , 'reaction' , 'message#alice' ],
[ '🐕' , 'Luke' , 'reaction' , 'message#alice' ],
[ '🐈' , 'Luke' , 'reaction' , 'message#alice' ],
[ '🛂' , 'Luke' , 'reaction' , 'message#alice' ],
[ '🕸' , 'Luke' , 'reaction' , 'message#alice' ],
[ '🏰' , 'Luke' , 'reaction' , 'message#alice' ],
[ '⚙️' , 'Luke' , 'reaction' , 'message#alice' ],
[ '🚨' , 'Luke' , 'reaction' , 'message#alice' ],
[ '👥' , 'Luke' , 'reaction' , 'message#alice' ],
[ '👍' , 'Paul' , 'reaction' , 'message#alice' ],
[ '👍' , 'Peter' , 'reaction' , 'message#alice' ],
[ '💜' , 'Matthew' , 'reaction' , 'message#alice' ],
[ '💜' , 'Mark' , 'reaction' , 'message#alice' ],
[ '💜' , 'Luke' , 'reaction' , 'message#alice' ],
2022-01-14 09:14:52 -05:00
],
[
'💜' => 3 ,
'👍' => 2 ,
],
2022-01-17 05:56:24 -05:00
false ,
2022-01-14 09:14:52 -05:00
],
];
}
2015-11-23 17:53:55 -05:00
}