2015-11-23 17:53:55 -05:00
< ? php
2025-05-05 03:36:34 -04:00
declare ( strict_types = 1 );
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 ;
2025-05-05 03:36:34 -04:00
use OCP\IUserManager ;
2022-06-13 11:22:36 -04:00
use OCP\Server ;
2026-01-06 08:11:50 -05:00
use PHPUnit\Framework\Attributes\DataProvider ;
use PHPUnit\Framework\Attributes\Group ;
use PHPUnit\Framework\MockObject\MockObject ;
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
*/
2026-01-06 08:11:50 -05:00
#[Group(name: 'DB')]
2016-05-18 12:55:44 -04:00
class ManagerTest extends TestCase {
2026-01-06 08:11:50 -05:00
private IDBConnection $connection ;
private IRootFolder & MockObject $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 ();
2025-06-12 12:31:58 -04:00
$this -> connection = Server :: get ( IDBConnection :: class );
2024-03-07 09:26:11 -05:00
$this -> rootFolder = $this -> createMock ( IRootFolder :: class );
2017-03-29 11:29:32 -04:00
2026-01-06 08:11:50 -05:00
/** @psalm-suppress DeprecatedMethod */
2017-03-29 11:29:32 -04:00
$sql = $this -> connection -> getDatabasePlatform () -> getTruncateTableSQL ( '`*PREFIX*comments`' );
$this -> connection -> prepare ( $sql ) -> execute ();
2026-01-06 08:11:50 -05:00
/** @psalm-suppress DeprecatedMethod */
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
}
2026-01-06 08:11:50 -05:00
protected function addDatabaseEntry ( ? string $parentId , ? string $topmostParentId , ? \DateTimeInterface $creationDT = null , ? \DateTimeInterface $latestChildDT = null , $objectId = null , ? \DateTimeInterface $expireDate = null ) : string {
2025-05-05 03:36:34 -04:00
$creationDT ? ? = new \DateTime ();
$latestChildDT ? ? = new \DateTime ( 'yesterday' );
$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
])
2025-05-05 03:36:34 -04:00
-> executeStatement ();
2015-11-23 17:53:55 -05:00
2026-01-06 08:11:50 -05:00
return ( string ) $qb -> getLastInsertId ();
2015-11-23 17:53:55 -05:00
}
2026-01-06 08:11:50 -05:00
protected function getManager () : Manager {
/** @psalm-suppress DeprecatedInterface No way around at the moment */
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
2024-09-15 16:32:31 -04:00
public function testGetCommentNotFound () : void {
2025-06-12 12:31:58 -04:00
$this -> expectException ( NotFoundException :: class );
2019-11-27 09:27:18 -05:00
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
$manager -> get ( '22' );
}
2020-08-11 15:32:18 -04:00
2024-09-15 16:32:31 -04: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' );
}
2024-09-15 16:32:31 -04:00
public function testGetComment () : void {
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
2025-05-05 03:36:34 -04:00
$creationDT = new \DateTime ( 'yesterday' );
$latestChildDT = new \DateTime ();
2015-11-23 17:53:55 -05:00
2026-01-06 08:11:50 -05: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 ( '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
])
2025-05-05 03:36:34 -04:00
-> executeStatement ();
2015-11-23 17:53:55 -05:00
2025-05-05 03:36:34 -04:00
$id = ( string ) $qb -> getLastInsertId ();
2015-11-23 17:53:55 -05:00
$comment = $manager -> get ( $id );
2025-05-05 03:36:34 -04:00
$this -> assertInstanceOf ( IComment :: class , $comment );
$this -> assertSame ( $id , $comment -> getId ());
$this -> assertSame ( '2' , $comment -> getParentId ());
$this -> assertSame ( '1' , $comment -> getTopmostParentId ());
$this -> assertSame ( 2 , $comment -> getChildrenCount ());
$this -> assertSame ( 'users' , $comment -> getActorType ());
$this -> assertSame ( 'alice' , $comment -> getActorId ());
$this -> assertSame ( 'nice one' , $comment -> getMessage ());
$this -> assertSame ( 'comment' , $comment -> getVerb ());
$this -> assertSame ( 'files' , $comment -> getObjectType ());
$this -> assertSame ( 'file64' , $comment -> getObjectId ());
$this -> assertEquals ( $creationDT -> getTimestamp (), $comment -> getCreationDateTime () -> getTimestamp ());
$this -> assertEquals ( $latestChildDT -> getTimestamp (), $comment -> getLatestChildDateTime () -> getTimestamp ());
2025-05-05 04:09:22 -04:00
$this -> assertEquals ( 'referenceId' , $comment -> getReferenceId ());
2025-05-05 03:36:34 -04:00
$this -> assertEquals ([ 'last_edit_actor_id' => 'admin' ], $comment -> getMetaData ());
2015-11-23 17:53:55 -05:00
}
2024-09-15 16:32:31 -04:00
public function testGetTreeNotFound () : void {
2025-06-12 12:31:58 -04:00
$this -> expectException ( NotFoundException :: class );
2019-11-27 09:27:18 -05:00
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
$manager -> getTree ( '22' );
}
2024-09-15 16:32:31 -04: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' );
}
2024-09-15 16:32:31 -04:00
public function testGetTree () : void {
2026-01-06 08:11:50 -05:00
$headId = $this -> addDatabaseEntry ( '0' , '0' );
2015-11-23 17:53:55 -05:00
$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
2025-05-05 03:36:34 -04:00
$this -> assertArrayHasKey ( 'comment' , $tree );
$this -> assertInstanceOf ( IComment :: class , $tree [ 'comment' ]);
2026-01-06 08:11:50 -05:00
$this -> assertSame ( $headId , $tree [ 'comment' ] -> getId ());
2025-05-05 03:36:34 -04:00
$this -> assertArrayHasKey ( 'replies' , $tree );
$this -> assertCount ( 3 , $tree [ 'replies' ]);
2015-11-23 17:53:55 -05:00
// one level deep
2017-03-29 11:29:32 -04:00
foreach ( $tree [ 'replies' ] as $reply ) {
2025-05-05 03:36:34 -04:00
$this -> assertInstanceOf ( IComment :: class , $reply [ 'comment' ]);
$this -> assertSame (( string ) $id , $reply [ 'comment' ] -> getId ());
$this -> assertCount ( 0 , $reply [ 'replies' ]);
2015-11-23 17:53:55 -05:00
$id -- ;
}
}
2024-09-15 16:32:31 -04:00
public function testGetTreeNoReplies () : void {
2026-01-06 08:11:50 -05:00
$id = $this -> addDatabaseEntry ( '0' , '0' );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
$tree = $manager -> getTree ( $id );
// Verifying the root comment
2025-05-05 03:36:34 -04:00
$this -> assertArrayHasKey ( 'comment' , $tree );
$this -> assertInstanceOf ( IComment :: class , $tree [ 'comment' ]);
2026-01-06 08:11:50 -05:00
$this -> assertSame ( $id , $tree [ 'comment' ] -> getId ());
2025-05-05 03:36:34 -04:00
$this -> assertArrayHasKey ( 'replies' , $tree );
$this -> assertCount ( 0 , $tree [ 'replies' ]);
2015-11-23 17:53:55 -05:00
}
2024-09-15 16:32:31 -04:00
public function testGetTreeWithLimitAndOffset () : void {
2026-01-06 08:11:50 -05:00
$headId = $this -> addDatabaseEntry ( '0' , '0' );
2015-11-23 17:53:55 -05:00
$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 ) {
2026-01-06 08:11:50 -05:00
$tree = $manager -> getTree ( $headId , 2 , $offset );
2015-11-23 17:53:55 -05:00
// Verifying the root comment
2025-05-05 03:36:34 -04:00
$this -> assertArrayHasKey ( 'comment' , $tree );
$this -> assertInstanceOf ( IComment :: class , $tree [ 'comment' ]);
2026-01-06 08:11:50 -05:00
$this -> assertSame ( $headId , $tree [ 'comment' ] -> getId ());
2025-05-05 03:36:34 -04:00
$this -> assertArrayHasKey ( 'replies' , $tree );
$this -> assertCount ( 2 , $tree [ 'replies' ]);
2015-11-23 17:53:55 -05:00
// one level deep
foreach ( $tree [ 'replies' ] as $reply ) {
2025-05-05 03:36:34 -04:00
$this -> assertInstanceOf ( IComment :: class , $reply [ 'comment' ]);
2026-01-06 08:11:50 -05:00
$this -> assertSame (( string ) $idToVerify , ( string ) $reply [ 'comment' ] -> getId ());
2025-05-05 03:36:34 -04:00
$this -> assertCount ( 0 , $reply [ 'replies' ]);
2015-11-23 17:53:55 -05:00
$idToVerify -- ;
}
}
}
2024-09-15 16:32:31 -04:00
public function testGetForObject () : void {
2026-01-06 08:11:50 -05:00
$this -> addDatabaseEntry ( '0' , '0' );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
2016-02-03 13:28:15 -05:00
$comments = $manager -> getForObject ( 'files' , 'file64' );
2015-11-23 17:53:55 -05:00
2025-05-05 03:36:34 -04:00
$this -> assertIsArray ( $comments );
$this -> assertCount ( 1 , $comments );
$this -> assertInstanceOf ( IComment :: class , $comments [ 0 ]);
$this -> assertSame ( 'nice one' , $comments [ 0 ] -> getMessage ());
2015-11-23 17:53:55 -05:00
}
2024-09-15 16:32:31 -04:00
public function testGetForObjectWithLimitAndOffset () : void {
2026-01-06 08:11:50 -05:00
$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 ());
2015-11-23 17:53:55 -05:00
$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
2025-05-05 03:36:34 -04:00
$this -> assertIsArray ( $comments );
foreach ( $comments as $key => $comment ) {
$this -> assertInstanceOf ( IComment :: class , $comment );
$this -> assertSame ( 'nice one' , $comment -> getMessage ());
$this -> assertSame (( string ) $idToVerify , $comment -> getId (), 'ID wrong for comment ' . $key . ' on offset: ' . $offset );
2015-11-23 17:53:55 -05:00
$idToVerify -- ;
}
$offset += 3 ;
2017-03-29 11:29:32 -04:00
} while ( count ( $comments ) > 0 );
2015-11-23 17:53:55 -05:00
}
2024-09-15 16:32:31 -04:00
public function testGetForObjectWithDateTimeConstraint () : void {
2026-01-06 08:11:50 -05:00
$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' ));
2015-11-23 17:53:55 -05:00
$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
2025-05-05 03:36:34 -04:00
$this -> assertCount ( 2 , $comments );
2026-01-06 08:11:50 -05:00
$this -> assertSame ( $id2 , $comments [ 0 ] -> getId ());
$this -> assertSame ( $id1 , $comments [ 1 ] -> getId ());
2015-11-23 17:53:55 -05:00
}
2024-09-15 16:32:31 -04:00
public function testGetForObjectWithLimitAndOffsetAndDateTimeConstraint () : void {
2026-01-06 08:11:50 -05:00
$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 ());
2015-11-23 17:53:55 -05:00
$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
2025-05-05 03:36:34 -04:00
$this -> assertIsArray ( $comments );
2017-03-29 11:29:32 -04:00
foreach ( $comments as $comment ) {
2025-05-05 03:36:34 -04:00
$this -> assertInstanceOf ( IComment :: class , $comment );
$this -> assertSame ( 'nice one' , $comment -> getMessage ());
$this -> assertSame (( string ) $idToVerify , $comment -> getId ());
$this -> assertGreaterThanOrEqual ( 4 , $comment -> getId ());
2015-11-23 17:53:55 -05:00
$idToVerify -- ;
}
$offset += 3 ;
2017-03-29 11:29:32 -04:00
} while ( count ( $comments ) > 0 );
2015-11-23 17:53:55 -05:00
}
2024-09-15 16:32:31 -04:00
public function testGetNumberOfCommentsForObject () : void {
2017-03-29 11:29:32 -04:00
for ( $i = 1 ; $i < 5 ; $i ++ ) {
2026-01-06 08:11:50 -05:00
$this -> addDatabaseEntry ( '0' , '0' );
2015-11-23 17:53:55 -05:00
}
$manager = $this -> getManager ();
$amount = $manager -> getNumberOfCommentsForObject ( 'untype' , '00' );
2025-05-05 03:36:34 -04:00
$this -> assertSame ( 0 , $amount );
2015-11-23 17:53:55 -05:00
2016-02-03 13:28:15 -05:00
$amount = $manager -> getNumberOfCommentsForObject ( 'files' , 'file64' );
2025-05-05 03:36:34 -04:00
$this -> assertSame ( 4 , $amount );
2015-11-23 17:53:55 -05:00
}
2024-09-15 16:32:31 -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
2026-01-06 08:11:50 -05:00
$this -> addDatabaseEntry ( '0' , '0' , null , null , $fileIds [ 1 ]);
2017-07-22 06:21:00 -04:00
for ( $i = 0 ; $i < 4 ; $i ++ ) {
2026-01-06 08:11:50 -05:00
$this -> addDatabaseEntry ( '0' , '0' , null , null , $fileIds [ $i ]);
2017-07-22 06:21:00 -04:00
}
2026-01-06 08:11:50 -05:00
$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 ();
2024-08-23 09:10:27 -04:00
$manager -> setReadMark ( 'files' , ( string ) $fileIds [ 0 ], ( new \DateTime ()) -> modify ( '-1 days' ), $user );
$manager -> setReadMark ( 'files' , ( string ) $fileIds [ 2 ], ( new \DateTime ()), $user );
2017-07-22 06:21:00 -04:00
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 );
}
2026-01-06 08:11:50 -05:00
#[DataProvider(methodName: 'dataGetForObjectSince')]
2025-05-05 03:36:34 -04:00
public function testGetForObjectSince ( ? int $lastKnown , string $order , int $limit , int $resultFrom , int $resultTo ) : void {
2018-04-10 04:50:57 -04:00
$ids = [];
2026-01-06 08:11:50 -05:00
$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' );
2018-04-10 04:50:57 -04:00
$manager = $this -> getManager ();
2026-01-06 08:11:50 -05:00
$comments = $manager -> getForObjectSince ( 'files' , 'file64' , ( $lastKnown === null ? 0 : ( int ) $ids [ $lastKnown ]), $order , $limit );
2018-04-10 04:50:57 -04:00
$expected = array_slice ( $ids , $resultFrom , $resultTo - $resultFrom + 1 );
if ( $order === 'desc' ) {
$expected = array_reverse ( $expected );
}
2026-01-06 08:11:50 -05:00
$this -> assertSame ( $expected , array_map ( static fn ( IComment $c ) : string => $c -> getId (), $comments ));
2018-04-10 04:50:57 -04:00
}
2025-05-05 03:36:34 -04:00
public static function dataGetForObjectSince () : array {
2018-04-10 04:50:57 -04:00
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 ],
];
}
2025-05-05 03:36:34 -04:00
public static function invalidCreateArgsProvider () : array {
2015-11-23 17:53:55 -05:00
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 ],
];
}
2026-01-06 08:11:50 -05:00
#[DataProvider(methodName: 'invalidCreateArgsProvider')]
2025-05-05 03:36:34 -04:00
public function testCreateCommentInvalidArguments ( string | int $aType , string | int $aId , string | int $oType , string | int $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 );
}
2024-09-15 16:32:31 -04:00
public function testCreateComment () : void {
2015-11-23 17:53:55 -05:00
$actorType = 'bot' ;
$actorId = 'bob' ;
$objectType = 'weather' ;
$objectId = 'bielefeld' ;
$comment = $this -> getManager () -> create ( $actorType , $actorId , $objectType , $objectId );
2025-05-05 03:36:34 -04:00
$this -> assertInstanceOf ( IComment :: class , $comment );
$this -> assertSame ( $actorType , $comment -> getActorType ());
$this -> assertSame ( $actorId , $comment -> getActorId ());
$this -> assertSame ( $objectType , $comment -> getObjectType ());
$this -> assertSame ( $objectId , $comment -> getObjectId ());
2015-11-23 17:53:55 -05:00
}
2020-08-11 15:32:18 -04:00
2024-09-15 16:32:31 -04:00
public function testDelete () : void {
2025-06-12 12:31:58 -04:00
$this -> expectException ( NotFoundException :: class );
2019-11-27 09:27:18 -05:00
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 );
2026-01-06 08:11:50 -05:00
$id = $this -> addDatabaseEntry ( '0' , '0' );
2015-11-23 17:53:55 -05:00
$comment = $manager -> get ( $id );
2025-05-05 03:36:34 -04:00
$this -> assertInstanceOf ( IComment :: class , $comment );
2015-11-23 17:53:55 -05:00
$done = $manager -> delete ( $id );
$this -> assertTrue ( $done );
$manager -> get ( $id );
}
2026-01-06 08:11:50 -05:00
#[DataProvider(methodName: 'providerTestSave')]
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 ;
}
2025-05-05 03:36:34 -04:00
public static function providerTestSave () : array {
2022-01-11 10:20:51 -05:00
return [
2025-05-05 03:36:34 -04:00
[ 'very beautiful, I am impressed!' , 'alice' , 'comment' , null ],
2022-01-11 10:20:51 -05:00
];
2015-11-23 17:53:55 -05:00
}
2024-09-15 16:32:31 -04:00
public function testSaveUpdate () : void {
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!' )
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
2024-09-15 16:32:31 -04:00
public function testSaveUpdateException () : void {
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 ());
2025-05-05 03:36:34 -04:00
2015-11-23 17:53:55 -05:00
$comment -> setMessage ( 'very beautiful, I am really so much impressed!' );
2025-06-12 12:31:58 -04:00
$this -> expectException ( NotFoundException :: class );
2015-11-23 17:53:55 -05:00
$manager -> save ( $comment );
}
2020-08-11 15:32:18 -04:00
2024-09-15 16:32:31 -04:00
public function testSaveIncomplete () : void {
2019-11-27 09:27:18 -05:00
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' );
2025-05-05 03:36:34 -04:00
$this -> expectException ( \UnexpectedValueException :: class );
2015-11-23 17:53:55 -05:00
$manager -> save ( $comment );
}
2024-09-15 16:32:31 -04:00
public function testSaveAsChild () : void {
2026-01-06 08:11:50 -05:00
$id = $this -> addDatabaseEntry ( '0' , '0' );
2015-11-23 17:53:55 -05:00
$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' )
2025-05-05 03:36:34 -04:00
-> setParentId ( $id )
2017-03-29 11:29:32 -04:00
-> 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 );
2025-05-05 03:36:34 -04:00
$this -> assertSame ( $id , $comment -> getTopmostParentId ());
$parentComment = $manager -> get ( $id );
$this -> assertSame ( $i + 1 , $parentComment -> getChildrenCount ());
$this -> assertEquals ( $comment -> getCreationDateTime () -> getTimestamp (), $parentComment -> getLatestChildDateTime () -> getTimestamp ());
2015-11-23 17:53:55 -05:00
}
}
2025-05-05 03:36:34 -04:00
public static function invalidActorArgsProvider () : array {
2015-11-23 17:53:55 -05:00
return
2017-03-29 11:29:32 -04:00
[
[ '' , '' ],
[ 1 , 'alice' ],
[ 'users' , 1 ],
];
2015-11-23 17:53:55 -05:00
}
2026-01-06 08:11:50 -05:00
#[DataProvider(methodName: 'invalidActorArgsProvider')]
2025-05-05 03:36:34 -04:00
public function testDeleteReferencesOfActorInvalidInput ( string | int $type , string | int $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 );
}
2024-09-15 16:32:31 -04:00
public function testDeleteReferencesOfActor () : void {
2015-11-23 17:53:55 -05:00
$ids = [];
2026-01-06 08:11:50 -05:00
$ids [] = $this -> addDatabaseEntry ( '0' , '0' );
$ids [] = $this -> addDatabaseEntry ( '0' , '0' );
$ids [] = $this -> addDatabaseEntry ( '0' , '0' );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
// just to make sure they are really set, with correct actor data
2026-01-06 08:11:50 -05:00
$comment = $manager -> get ( $ids [ 1 ]);
2025-05-05 03:36:34 -04:00
$this -> assertSame ( 'users' , $comment -> getActorType ());
$this -> assertSame ( 'alice' , $comment -> getActorId ());
2015-11-23 17:53:55 -05:00
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 ) {
2026-01-06 08:11:50 -05:00
$comment = $manager -> get ( $id );
2025-05-05 03:36:34 -04:00
$this -> assertSame ( ICommentsManager :: DELETED_USER , $comment -> getActorType ());
$this -> assertSame ( ICommentsManager :: DELETED_USER , $comment -> getActorId ());
2015-11-23 17:53:55 -05:00
}
// 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 );
}
2024-09-15 16:32:31 -04:00
public function testDeleteReferencesOfActorWithUserManagement () : void {
2025-06-12 12:31:58 -04:00
$user = Server :: get ( IUserManager :: class ) -> createUser ( 'xenia' , 'NotAnEasyPassword123456+' );
2025-05-05 03:36:34 -04:00
$this -> assertInstanceOf ( IUser :: class , $user );
2015-11-23 17:53:55 -05:00
2025-06-12 12:31:58 -04:00
$manager = 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 );
2025-05-05 03:36:34 -04:00
$this -> assertSame ( ICommentsManager :: DELETED_USER , $comment -> getActorType ());
$this -> assertSame ( ICommentsManager :: DELETED_USER , $comment -> getActorId ());
2015-11-23 17:53:55 -05:00
}
2025-05-05 03:36:34 -04:00
public static function invalidObjectArgsProvider () : array {
2015-11-23 17:53:55 -05:00
return
2017-03-29 11:29:32 -04:00
[
[ '' , '' ],
[ 1 , 'file64' ],
[ 'files' , 1 ],
];
2015-11-23 17:53:55 -05:00
}
2026-01-06 08:11:50 -05:00
#[DataProvider(methodName: 'invalidObjectArgsProvider')]
2025-05-05 03:36:34 -04:00
public function testDeleteCommentsAtObjectInvalidInput ( string | int $type , string | int $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 );
}
2024-09-15 16:32:31 -04:00
public function testDeleteCommentsAtObject () : void {
2015-11-23 17:53:55 -05:00
$ids = [];
2026-01-06 08:11:50 -05:00
$ids [] = $this -> addDatabaseEntry ( '0' , '0' );
$ids [] = $this -> addDatabaseEntry ( '0' , '0' );
$ids [] = $this -> addDatabaseEntry ( '0' , '0' );
2015-11-23 17:53:55 -05:00
$manager = $this -> getManager ();
// just to make sure they are really set, with correct actor data
2026-01-06 08:11:50 -05:00
$comment = $manager -> get ( $ids [ 1 ]);
2025-05-05 03:36:34 -04:00
$this -> assertSame ( 'files' , $comment -> getObjectType ());
$this -> assertSame ( 'file64' , $comment -> getObjectId ());
2015-11-23 17:53:55 -05:00
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 {
2026-01-06 08:11:50 -05:00
$manager -> get ( $id );
2025-05-05 03:36:34 -04:00
} catch ( NotFoundException ) {
2015-11-23 17:53:55 -05:00
$verified ++ ;
}
}
2025-05-05 03:36:34 -04:00
$this -> assertSame ( 3 , $verified );
2015-11-23 17:53:55 -05:00
// 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 = [];
2026-01-06 08:11:50 -05:00
$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' ));
/** @psalm-suppress DeprecatedInterface No way around at the moment */
2022-06-13 11:22:36 -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-06-13 11:22:36 -04:00
);
// just to make sure they are really set, with correct actor data
2026-01-06 08:11:50 -05:00
$comment = $manager -> get ( $ids [ 1 ]);
2025-05-05 03:36:34 -04:00
$this -> assertSame ( 'files' , $comment -> getObjectType ());
$this -> assertSame ( 'file64' , $comment -> getObjectId ());
2022-06-13 11:22:36 -04:00
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 {
2026-01-06 08:11:50 -05:00
$manager -> get ( $id );
2022-06-13 11:22:36 -04:00
$exists ++ ;
2025-05-05 03:36:34 -04:00
} catch ( NotFoundException ) {
2022-06-13 11:22:36 -04:00
$deleted ++ ;
}
}
2025-05-05 03:36:34 -04:00
$this -> assertSame ( 3 , $exists );
$this -> assertSame ( 3 , $deleted );
2022-06-13 11:22:36 -04:00
// 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 = [];
2026-01-06 08:11:50 -05:00
$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' ));
$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 ());
/** @psalm-suppress DeprecatedInterface No way around at the moment */
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 {
2026-01-06 08:11:50 -05:00
$manager -> get ( $id );
2022-07-01 09:53:37 -04:00
$exists ++ ;
2025-05-05 03:36:34 -04:00
} catch ( NotFoundException ) {
2022-07-01 09:53:37 -04:00
$deleted ++ ;
}
}
2025-05-05 03:36:34 -04:00
$this -> assertSame ( 0 , $exists );
$this -> assertSame ( 6 , $deleted );
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 );
}
2024-09-15 16:32:31 -04: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
2025-05-05 03:36:34 -04:00
$this -> assertEquals ( $dateTimeSet -> getTimestamp (), $dateTimeGet -> getTimestamp ());
2016-01-27 12:30:09 -05:00
}
2024-09-15 16:32:31 -04: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
2025-05-05 03:36:34 -04:00
$this -> assertEquals ( $dateTimeSet , $dateTimeGet );
2016-01-28 16:59:48 -05:00
}
2024-09-15 16:32:31 -04:00
public function testReadMarkDeleteUser () : void {
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 );
}
2024-09-15 16:32:31 -04:00
public function testReadMarkDeleteObject () : void {
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 );
}
2024-09-15 16:32:31 -04:00
public function testSendEvent () : void {
2026-01-06 08:11:50 -05:00
/** @psalm-suppress DeprecatedInterface Test for deprecated interface */
2025-05-05 03:36:34 -04:00
$handler1 = $this -> createMock ( ICommentsEventHandler :: class );
2016-10-12 12:06:22 -04:00
$handler1 -> expects ( $this -> exactly ( 4 ))
2016-05-09 04:02:07 -04:00
-> method ( 'handle' );
2026-01-06 08:11:50 -05:00
/** @psalm-suppress DeprecatedInterface Test for deprecated interface */
2025-05-05 03:36:34 -04:00
$handler2 = $this -> createMock ( ICommentsEventHandler :: class );
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 ());
}
2024-09-15 16:32:31 -04:00
public function testResolveDisplayName () : void {
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 );
};
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
2024-09-15 16:32:31 -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 ();
2026-01-06 08:11:50 -05:00
$planetClosure = static fn ( string $name ) : string => ucfirst ( $name );
2016-10-16 14:28:36 -04:00
$manager -> registerDisplayNameResolver ( 'planet' , $planetClosure );
$manager -> registerDisplayNameResolver ( 'planet' , $planetClosure );
}
2020-08-11 15:32:18 -04:00
2024-09-15 16:32:31 -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 ();
2026-01-06 08:11:50 -05:00
$planetClosure = static fn ( string $name ) : string => ucfirst ( $name );
2016-10-16 14:28:36 -04:00
$manager -> registerDisplayNameResolver ( 'planet' , $planetClosure );
$manager -> resolveDisplayName ( 'galaxy' , 'sombrero' );
}
2024-09-15 16:32:31 -04:00
public function testResolveDisplayNameDirtyResolver () : void {
2016-10-16 14:28:36 -04:00
$manager = $this -> getManager ();
2026-01-06 08:11:50 -05:00
$planetClosure = static fn () : null => null ;
2016-10-16 14:28:36 -04:00
$manager -> registerDisplayNameResolver ( 'planet' , $planetClosure );
2025-05-05 03:36:34 -04:00
$this -> assertIsString ( $manager -> resolveDisplayName ( 'planet' , 'neptune' ));
}
private function skipIfNotSupport4ByteUTF () : void {
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' );
}
}
2026-01-06 08:11:50 -05:00
#[DataProvider(methodName: 'providerTestReactionAddAndDelete')]
2024-09-15 16:32:31 -04:00
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 ());
}
}
2025-05-05 03:36:34 -04:00
public static function providerTestReactionAddAndDelete () : array {
2022-01-11 10:20:51 -05:00
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
2022-01-11 10:20:51 -05:00
/**
2022-01-21 06:40:29 -05:00
* @ param array $data
2025-05-05 03:36:34 -04:00
* @ return array < string , IComment >
2022-01-11 10:20:51 -05:00
*/
2022-01-21 06:40:29 -05:00
private function proccessComments ( array $data ) : array {
2025-05-17 10:44:22 -04:00
$this -> connection -> beginTransaction ();
2025-05-05 03:36:34 -04:00
/** @var array<string, IComment> $comments */
2022-01-21 06:40:29 -05:00
$comments = [];
foreach ( $data as $comment ) {
[ $message , $actorId , $verb , $parentText ] = $comment ;
2022-01-11 10:20:51 -05:00
$parentId = null ;
if ( $parentText ) {
2026-01-06 08:11:50 -05:00
$parentId = $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
}
2025-05-17 10:44:22 -04:00
$this -> connection -> commit ();
2022-01-21 06:40:29 -05:00
return $comments ;
}
2026-01-06 08:11:50 -05:00
#[DataProvider(methodName: 'providerTestRetrieveAllReactions')]
2024-09-15 16:32:31 -04:00
public function testRetrieveAllReactions ( array $comments , array $expected ) : void {
2022-01-21 06:40:29 -05:00
$this -> skipIfNotSupport4ByteUTF ();
$manager = $this -> getManager ();
$processedComments = $this -> proccessComments ( $comments );
$comment = reset ( $processedComments );
2025-05-05 03:36:34 -04:00
$all = $manager -> retrieveAllReactions (( int ) $comment -> getId ());
$actual = array_map ( static function ( IComment $row ) : array {
2022-01-11 10:20:51 -05:00
return [
2025-05-05 03:36:34 -04:00
$row -> getActorId (),
$row -> getMessage (),
2022-01-11 10:20:51 -05:00
];
}, $all );
2025-05-14 05:42:52 -04:00
usort ( $actual , static fn ( array $a , array $b ) : int => $a [ 1 ] <=> $b [ 1 ]);
usort ( $expected , static fn ( array $a , array $b ) : int => $a [ 1 ] <=> $b [ 1 ]);
2022-01-11 10:20:51 -05:00
$this -> assertEqualsCanonicalizing ( $expected , $actual );
}
2025-05-05 03:36:34 -04:00
public static function providerTestRetrieveAllReactions () : array {
2022-01-11 10:20:51 -05:00
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
];
}
2026-01-06 08:11:50 -05:00
#[DataProvider(methodName: 'providerTestRetrieveAllReactionsWithSpecificReaction')]
2024-09-15 16:32:31 -04:00
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 );
2025-05-05 03:36:34 -04:00
$all = $manager -> retrieveAllReactionsWithSpecificReaction (( int ) $comment -> getId (), $reaction );
$actual = array_map ( static function ( IComment $row ) : array {
2022-01-11 10:20:51 -05:00
return [
2025-05-05 03:36:34 -04:00
$row -> getActorId (),
$row -> getMessage (),
2022-01-11 10:20:51 -05:00
];
}, $all );
$this -> assertEqualsCanonicalizing ( $expected , $actual );
}
2025-05-05 03:36:34 -04:00
public static function providerTestRetrieveAllReactionsWithSpecificReaction () : array {
2022-01-11 10:20:51 -05:00
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' ],
],
],
];
}
2026-01-06 08:11:50 -05:00
#[DataProvider(methodName: 'providerTestGetReactionComment')]
2024-09-15 16:32:31 -04: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 ) {
2025-06-12 12:31:58 -04:00
$this -> expectException ( NotFoundException :: class );
2022-01-11 10:20:51 -05:00
}
2022-01-21 06:40:29 -05:00
$comment = $processedComments [ $expected [ 'message' ] . '#' . $expected [ 'actorId' ]];
2025-05-05 03:36:34 -04:00
$actual = $manager -> getReactionComment (( int ) $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
}
2025-05-05 03:36:34 -04:00
public static function providerTestGetReactionComment () : array {
2022-01-11 10:20:51 -05:00
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
2026-01-06 08:11:50 -05:00
#[DataProvider(methodName: 'providerTestReactionMessageSize')]
2025-05-05 03:36:34 -04:00
public function testReactionMessageSize ( string $reactionString , bool $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 );
}
2025-05-05 03:36:34 -04:00
public static function providerTestReactionMessageSize () : array {
2022-01-14 09:14:52 -05:00
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
];
}
2026-01-06 08:11:50 -05:00
#[DataProvider(methodName: 'providerTestReactionsSummarizeOrdered')]
2024-09-15 16:32:31 -04: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
}
2025-05-05 03:36:34 -04:00
public static function providerTestReactionsSummarizeOrdered () : array {
2022-01-14 09:14:52 -05:00
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
}