Completed
Push — master ( b82245...835746 )
by Joas
33:37 queued 24s
created
tests/lib/Comments/ManagerTest.php 1 patch
Indentation   +2554 added lines, -2554 removed lines patch added patch discarded remove patch
@@ -32,2571 +32,2571 @@
 block discarded – undo
32 32
  * @group DB
33 33
  */
34 34
 class ManagerTest extends TestCase {
35
-	/** @var IDBConnection */
36
-	private $connection;
37
-	/** @var \PHPUnit\Framework\MockObject\MockObject|IRootFolder */
38
-	private $rootFolder;
39
-
40
-	protected function setUp(): void {
41
-		parent::setUp();
42
-
43
-		$this->connection = \OC::$server->getDatabaseConnection();
44
-		$this->rootFolder = $this->createMock(IRootFolder::class);
45
-
46
-		$sql = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*comments`');
47
-		$this->connection->prepare($sql)->execute();
48
-		$sql = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*reactions`');
49
-		$this->connection->prepare($sql)->execute();
50
-	}
51
-
52
-	protected function addDatabaseEntry($parentId, $topmostParentId, $creationDT = null, $latestChildDT = null, $objectId = null, $expireDate = null) {
53
-		if (is_null($creationDT)) {
54
-			$creationDT = new \DateTime();
55
-		}
56
-		if (is_null($latestChildDT)) {
57
-			$latestChildDT = new \DateTime('yesterday');
58
-		}
59
-		if (is_null($objectId)) {
60
-			$objectId = 'file64';
61
-		}
62
-
63
-		$qb = $this->connection->getQueryBuilder();
64
-		$qb
65
-			->insert('comments')
66
-			->values([
67
-				'parent_id' => $qb->createNamedParameter($parentId),
68
-				'topmost_parent_id' => $qb->createNamedParameter($topmostParentId),
69
-				'children_count' => $qb->createNamedParameter(2),
70
-				'actor_type' => $qb->createNamedParameter('users'),
71
-				'actor_id' => $qb->createNamedParameter('alice'),
72
-				'message' => $qb->createNamedParameter('nice one'),
73
-				'verb' => $qb->createNamedParameter('comment'),
74
-				'creation_timestamp' => $qb->createNamedParameter($creationDT, IQueryBuilder::PARAM_DATETIME_MUTABLE),
75
-				'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, IQueryBuilder::PARAM_DATETIME_MUTABLE),
76
-				'object_type' => $qb->createNamedParameter('files'),
77
-				'object_id' => $qb->createNamedParameter($objectId),
78
-				'expire_date' => $qb->createNamedParameter($expireDate, IQueryBuilder::PARAM_DATETIME_MUTABLE),
79
-				'reference_id' => $qb->createNamedParameter('referenceId'),
80
-				'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])),
81
-			])
82
-			->execute();
83
-
84
-		return $qb->getLastInsertId();
85
-	}
86
-
87
-	protected function getManager() {
88
-		return new Manager(
89
-			$this->connection,
90
-			$this->createMock(LoggerInterface::class),
91
-			$this->createMock(IConfig::class),
92
-			$this->createMock(ITimeFactory::class),
93
-			new EmojiHelper($this->connection),
94
-			$this->createMock(IInitialStateService::class),
95
-			$this->rootFolder,
96
-			$this->createMock(IEventDispatcher::class),
97
-		);
98
-	}
99
-
100
-
101
-	public function testGetCommentNotFound(): void {
102
-		$this->expectException(\OCP\Comments\NotFoundException::class);
103
-
104
-		$manager = $this->getManager();
105
-		$manager->get('22');
106
-	}
107
-
108
-
109
-	public function testGetCommentNotFoundInvalidInput(): void {
110
-		$this->expectException(\InvalidArgumentException::class);
111
-
112
-		$manager = $this->getManager();
113
-		$manager->get('unexisting22');
114
-	}
115
-
116
-	public function testGetComment(): void {
117
-		$manager = $this->getManager();
118
-
119
-		$creationDT = new \DateTime();
120
-		$latestChildDT = new \DateTime('yesterday');
121
-
122
-		$qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
123
-		$qb
124
-			->insert('comments')
125
-			->values([
126
-				'parent_id' => $qb->createNamedParameter('2'),
127
-				'topmost_parent_id' => $qb->createNamedParameter('1'),
128
-				'children_count' => $qb->createNamedParameter(2),
129
-				'actor_type' => $qb->createNamedParameter('users'),
130
-				'actor_id' => $qb->createNamedParameter('alice'),
131
-				'message' => $qb->createNamedParameter('nice one'),
132
-				'verb' => $qb->createNamedParameter('comment'),
133
-				'creation_timestamp' => $qb->createNamedParameter($creationDT, 'datetime'),
134
-				'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, 'datetime'),
135
-				'object_type' => $qb->createNamedParameter('files'),
136
-				'object_id' => $qb->createNamedParameter('file64'),
137
-				'reference_id' => $qb->createNamedParameter('referenceId'),
138
-				'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])),
139
-			])
140
-			->execute();
141
-
142
-		$id = strval($qb->getLastInsertId());
143
-
144
-		$comment = $manager->get($id);
145
-		$this->assertTrue($comment instanceof IComment);
146
-		$this->assertSame($comment->getId(), $id);
147
-		$this->assertSame($comment->getParentId(), '2');
148
-		$this->assertSame($comment->getTopmostParentId(), '1');
149
-		$this->assertSame($comment->getChildrenCount(), 2);
150
-		$this->assertSame($comment->getActorType(), 'users');
151
-		$this->assertSame($comment->getActorId(), 'alice');
152
-		$this->assertSame($comment->getMessage(), 'nice one');
153
-		$this->assertSame($comment->getVerb(), 'comment');
154
-		$this->assertSame($comment->getObjectType(), 'files');
155
-		$this->assertSame($comment->getObjectId(), 'file64');
156
-		$this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $creationDT->getTimestamp());
157
-		$this->assertEquals($comment->getLatestChildDateTime(), $latestChildDT);
158
-		$this->assertEquals($comment->getReferenceId(), 'referenceId');
159
-		$this->assertEquals($comment->getMetaData(), ['last_edit_actor_id' => 'admin']);
160
-	}
161
-
162
-
163
-	public function testGetTreeNotFound(): void {
164
-		$this->expectException(\OCP\Comments\NotFoundException::class);
165
-
166
-		$manager = $this->getManager();
167
-		$manager->getTree('22');
168
-	}
169
-
170
-
171
-	public function testGetTreeNotFoundInvalidIpnut(): void {
172
-		$this->expectException(\InvalidArgumentException::class);
173
-
174
-		$manager = $this->getManager();
175
-		$manager->getTree('unexisting22');
176
-	}
177
-
178
-	public function testGetTree(): void {
179
-		$headId = $this->addDatabaseEntry(0, 0);
180
-
181
-		$this->addDatabaseEntry($headId, $headId, new \DateTime('-3 hours'));
182
-		$this->addDatabaseEntry($headId, $headId, new \DateTime('-2 hours'));
183
-		$id = $this->addDatabaseEntry($headId, $headId, new \DateTime('-1 hour'));
184
-
185
-		$manager = $this->getManager();
186
-		$tree = $manager->getTree($headId);
187
-
188
-		// Verifying the root comment
189
-		$this->assertTrue(isset($tree['comment']));
190
-		$this->assertTrue($tree['comment'] instanceof IComment);
191
-		$this->assertSame($tree['comment']->getId(), strval($headId));
192
-		$this->assertTrue(isset($tree['replies']));
193
-		$this->assertSame(count($tree['replies']), 3);
194
-
195
-		// one level deep
196
-		foreach ($tree['replies'] as $reply) {
197
-			$this->assertTrue($reply['comment'] instanceof IComment);
198
-			$this->assertSame($reply['comment']->getId(), strval($id));
199
-			$this->assertSame(count($reply['replies']), 0);
200
-			$id--;
201
-		}
202
-	}
203
-
204
-	public function testGetTreeNoReplies(): void {
205
-		$id = $this->addDatabaseEntry(0, 0);
206
-
207
-		$manager = $this->getManager();
208
-		$tree = $manager->getTree($id);
209
-
210
-		// Verifying the root comment
211
-		$this->assertTrue(isset($tree['comment']));
212
-		$this->assertTrue($tree['comment'] instanceof IComment);
213
-		$this->assertSame($tree['comment']->getId(), strval($id));
214
-		$this->assertTrue(isset($tree['replies']));
215
-		$this->assertSame(count($tree['replies']), 0);
216
-
217
-		// one level deep
218
-		foreach ($tree['replies'] as $reply) {
219
-			throw new \Exception('This ain`t happen');
220
-		}
221
-	}
222
-
223
-	public function testGetTreeWithLimitAndOffset(): void {
224
-		$headId = $this->addDatabaseEntry(0, 0);
225
-
226
-		$this->addDatabaseEntry($headId, $headId, new \DateTime('-3 hours'));
227
-		$this->addDatabaseEntry($headId, $headId, new \DateTime('-2 hours'));
228
-		$this->addDatabaseEntry($headId, $headId, new \DateTime('-1 hour'));
229
-		$idToVerify = $this->addDatabaseEntry($headId, $headId, new \DateTime());
230
-
231
-		$manager = $this->getManager();
232
-
233
-		for ($offset = 0; $offset < 3; $offset += 2) {
234
-			$tree = $manager->getTree(strval($headId), 2, $offset);
235
-
236
-			// Verifying the root comment
237
-			$this->assertTrue(isset($tree['comment']));
238
-			$this->assertTrue($tree['comment'] instanceof IComment);
239
-			$this->assertSame($tree['comment']->getId(), strval($headId));
240
-			$this->assertTrue(isset($tree['replies']));
241
-			$this->assertSame(count($tree['replies']), 2);
242
-
243
-			// one level deep
244
-			foreach ($tree['replies'] as $reply) {
245
-				$this->assertTrue($reply['comment'] instanceof IComment);
246
-				$this->assertSame($reply['comment']->getId(), strval($idToVerify));
247
-				$this->assertSame(count($reply['replies']), 0);
248
-				$idToVerify--;
249
-			}
250
-		}
251
-	}
252
-
253
-	public function testGetForObject(): void {
254
-		$this->addDatabaseEntry(0, 0);
255
-
256
-		$manager = $this->getManager();
257
-		$comments = $manager->getForObject('files', 'file64');
258
-
259
-		$this->assertTrue(is_array($comments));
260
-		$this->assertSame(count($comments), 1);
261
-		$this->assertTrue($comments[0] instanceof IComment);
262
-		$this->assertSame($comments[0]->getMessage(), 'nice one');
263
-	}
264
-
265
-	public function testGetForObjectWithLimitAndOffset(): void {
266
-		$this->addDatabaseEntry(0, 0, new \DateTime('-6 hours'));
267
-		$this->addDatabaseEntry(0, 0, new \DateTime('-5 hours'));
268
-		$this->addDatabaseEntry(1, 1, new \DateTime('-4 hours'));
269
-		$this->addDatabaseEntry(0, 0, new \DateTime('-3 hours'));
270
-		$this->addDatabaseEntry(2, 2, new \DateTime('-2 hours'));
271
-		$this->addDatabaseEntry(2, 2, new \DateTime('-1 hours'));
272
-		$idToVerify = $this->addDatabaseEntry(3, 1, new \DateTime());
273
-
274
-		$manager = $this->getManager();
275
-		$offset = 0;
276
-		do {
277
-			$comments = $manager->getForObject('files', 'file64', 3, $offset);
278
-
279
-			$this->assertTrue(is_array($comments));
280
-			foreach ($comments as $comment) {
281
-				$this->assertTrue($comment instanceof IComment);
282
-				$this->assertSame($comment->getMessage(), 'nice one');
283
-				$this->assertSame($comment->getId(), strval($idToVerify));
284
-				$idToVerify--;
285
-			}
286
-			$offset += 3;
287
-		} while (count($comments) > 0);
288
-	}
289
-
290
-	public function testGetForObjectWithDateTimeConstraint(): void {
291
-		$this->addDatabaseEntry(0, 0, new \DateTime('-6 hours'));
292
-		$this->addDatabaseEntry(0, 0, new \DateTime('-5 hours'));
293
-		$id1 = $this->addDatabaseEntry(0, 0, new \DateTime('-3 hours'));
294
-		$id2 = $this->addDatabaseEntry(2, 2, new \DateTime('-2 hours'));
295
-
296
-		$manager = $this->getManager();
297
-		$comments = $manager->getForObject('files', 'file64', 0, 0, new \DateTime('-4 hours'));
298
-
299
-		$this->assertSame(count($comments), 2);
300
-		$this->assertSame($comments[0]->getId(), strval($id2));
301
-		$this->assertSame($comments[1]->getId(), strval($id1));
302
-	}
303
-
304
-	public function testGetForObjectWithLimitAndOffsetAndDateTimeConstraint(): void {
305
-		$this->addDatabaseEntry(0, 0, new \DateTime('-7 hours'));
306
-		$this->addDatabaseEntry(0, 0, new \DateTime('-6 hours'));
307
-		$this->addDatabaseEntry(1, 1, new \DateTime('-5 hours'));
308
-		$this->addDatabaseEntry(0, 0, new \DateTime('-3 hours'));
309
-		$this->addDatabaseEntry(2, 2, new \DateTime('-2 hours'));
310
-		$this->addDatabaseEntry(2, 2, new \DateTime('-1 hours'));
311
-		$idToVerify = $this->addDatabaseEntry(3, 1, new \DateTime());
312
-
313
-		$manager = $this->getManager();
314
-		$offset = 0;
315
-		do {
316
-			$comments = $manager->getForObject('files', 'file64', 3, $offset, new \DateTime('-4 hours'));
317
-
318
-			$this->assertTrue(is_array($comments));
319
-			foreach ($comments as $comment) {
320
-				$this->assertTrue($comment instanceof IComment);
321
-				$this->assertSame($comment->getMessage(), 'nice one');
322
-				$this->assertSame($comment->getId(), strval($idToVerify));
323
-				$this->assertTrue(intval($comment->getId()) >= 4);
324
-				$idToVerify--;
325
-			}
326
-			$offset += 3;
327
-		} while (count($comments) > 0);
328
-	}
329
-
330
-	public function testGetNumberOfCommentsForObject(): void {
331
-		for ($i = 1; $i < 5; $i++) {
332
-			$this->addDatabaseEntry(0, 0);
333
-		}
334
-
335
-		$manager = $this->getManager();
336
-
337
-		$amount = $manager->getNumberOfCommentsForObject('untype', '00');
338
-		$this->assertSame($amount, 0);
339
-
340
-		$amount = $manager->getNumberOfCommentsForObject('files', 'file64');
341
-		$this->assertSame($amount, 4);
342
-	}
343
-
344
-	public function testGetNumberOfUnreadCommentsForFolder(): void {
345
-		$folder = $this->createMock(Folder::class);
346
-		$fileIds = range(1111, 1114);
347
-		$children = array_map(function (int $id) {
348
-			$file = $this->createMock(Folder::class);
349
-			$file->method('getId')
350
-				->willReturn($id);
351
-			return $file;
352
-		}, $fileIds);
353
-		$folder->method('getId')->willReturn(1000);
354
-		$folder->method('getDirectoryListing')->willReturn($children);
355
-		$this->rootFolder->method('getFirstNodeById')
356
-			->with($folder->getId())
357
-			->willReturn($folder);
358
-
359
-		// 2 comment for 1111 with 1 before read marker
360
-		// 2 comments for 1112 with no read marker
361
-		// 1 comment for 1113 before read marker
362
-		// 1 comment for 1114 with no read marker
363
-		$this->addDatabaseEntry(0, 0, null, null, $fileIds[1]);
364
-		for ($i = 0; $i < 4; $i++) {
365
-			$this->addDatabaseEntry(0, 0, null, null, $fileIds[$i]);
366
-		}
367
-		$this->addDatabaseEntry(0, 0, (new \DateTime())->modify('-2 days'), null, $fileIds[0]);
368
-		/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
369
-		$user = $this->createMock(IUser::class);
370
-		$user->expects($this->any())
371
-			->method('getUID')
372
-			->willReturn('comment_test');
373
-
374
-		$manager = $this->getManager();
375
-
376
-		$manager->setReadMark('files', (string)$fileIds[0], (new \DateTime())->modify('-1 days'), $user);
377
-		$manager->setReadMark('files', (string)$fileIds[2], (new \DateTime()), $user);
378
-
379
-		$amount = $manager->getNumberOfUnreadCommentsForFolder($folder->getId(), $user);
380
-		$this->assertEquals([
381
-			$fileIds[0] => 1,
382
-			$fileIds[1] => 2,
383
-			$fileIds[3] => 1,
384
-		], $amount);
385
-	}
386
-
387
-	/**
388
-	 * @dataProvider dataGetForObjectSince
389
-	 * @param $lastKnown
390
-	 * @param $order
391
-	 * @param $limit
392
-	 * @param $resultFrom
393
-	 * @param $resultTo
394
-	 */
395
-	public function testGetForObjectSince($lastKnown, $order, $limit, $resultFrom, $resultTo): void {
396
-		$ids = [];
397
-		$ids[] = $this->addDatabaseEntry(0, 0);
398
-		$ids[] = $this->addDatabaseEntry(0, 0);
399
-		$ids[] = $this->addDatabaseEntry(0, 0);
400
-		$ids[] = $this->addDatabaseEntry(0, 0);
401
-		$ids[] = $this->addDatabaseEntry(0, 0);
402
-
403
-		$manager = $this->getManager();
404
-		$comments = $manager->getForObjectSince('files', 'file64', ($lastKnown === null ? 0 : $ids[$lastKnown]), $order, $limit);
405
-
406
-		$expected = array_slice($ids, $resultFrom, $resultTo - $resultFrom + 1);
407
-		if ($order === 'desc') {
408
-			$expected = array_reverse($expected);
409
-		}
410
-
411
-		$this->assertSame($expected, array_map(function (IComment $c) {
412
-			return (int)$c->getId();
413
-		}, $comments));
414
-	}
415
-
416
-	public function dataGetForObjectSince() {
417
-		return [
418
-			[null, 'asc', 20, 0, 4],
419
-			[null, 'asc', 2, 0, 1],
420
-			[null, 'desc', 20, 0, 4],
421
-			[null, 'desc', 2, 3, 4],
422
-			[1, 'asc', 20, 2, 4],
423
-			[1, 'asc', 2, 2, 3],
424
-			[3, 'desc', 20, 0, 2],
425
-			[3, 'desc', 2, 1, 2],
426
-		];
427
-	}
428
-
429
-	public function invalidCreateArgsProvider() {
430
-		return [
431
-			['', 'aId-1', 'oType-1', 'oId-1'],
432
-			['aType-1', '', 'oType-1', 'oId-1'],
433
-			['aType-1', 'aId-1', '', 'oId-1'],
434
-			['aType-1', 'aId-1', 'oType-1', ''],
435
-			[1, 'aId-1', 'oType-1', 'oId-1'],
436
-			['aType-1', 1, 'oType-1', 'oId-1'],
437
-			['aType-1', 'aId-1', 1, 'oId-1'],
438
-			['aType-1', 'aId-1', 'oType-1', 1],
439
-		];
440
-	}
441
-
442
-	/**
443
-	 * @dataProvider invalidCreateArgsProvider
444
-	 * @param string $aType
445
-	 * @param string $aId
446
-	 * @param string $oType
447
-	 * @param string $oId
448
-	 */
449
-	public function testCreateCommentInvalidArguments($aType, $aId, $oType, $oId): void {
450
-		$this->expectException(\InvalidArgumentException::class);
451
-
452
-		$manager = $this->getManager();
453
-		$manager->create($aType, $aId, $oType, $oId);
454
-	}
455
-
456
-	public function testCreateComment(): void {
457
-		$actorType = 'bot';
458
-		$actorId = 'bob';
459
-		$objectType = 'weather';
460
-		$objectId = 'bielefeld';
461
-
462
-		$comment = $this->getManager()->create($actorType, $actorId, $objectType, $objectId);
463
-		$this->assertTrue($comment instanceof IComment);
464
-		$this->assertSame($comment->getActorType(), $actorType);
465
-		$this->assertSame($comment->getActorId(), $actorId);
466
-		$this->assertSame($comment->getObjectType(), $objectType);
467
-		$this->assertSame($comment->getObjectId(), $objectId);
468
-	}
469
-
470
-
471
-	public function testDelete(): void {
472
-		$this->expectException(\OCP\Comments\NotFoundException::class);
473
-
474
-		$manager = $this->getManager();
475
-
476
-		$done = $manager->delete('404');
477
-		$this->assertFalse($done);
478
-
479
-		$done = $manager->delete('%');
480
-		$this->assertFalse($done);
481
-
482
-		$done = $manager->delete('');
483
-		$this->assertFalse($done);
484
-
485
-		$id = strval($this->addDatabaseEntry(0, 0));
486
-		$comment = $manager->get($id);
487
-		$this->assertTrue($comment instanceof IComment);
488
-		$done = $manager->delete($id);
489
-		$this->assertTrue($done);
490
-		$manager->get($id);
491
-	}
492
-
493
-	/**
494
-	 * @dataProvider providerTestSave
495
-	 */
496
-	public function testSave(string $message, string $actorId, string $verb, ?string $parentId, ?string $id = ''): IComment {
497
-		$manager = $this->getManager();
498
-		$comment = new Comment();
499
-		$comment
500
-			->setId($id)
501
-			->setActor('users', $actorId)
502
-			->setObject('files', 'file64')
503
-			->setMessage($message)
504
-			->setVerb($verb);
505
-		if ($parentId) {
506
-			$comment->setParentId($parentId);
507
-		}
508
-
509
-		$saveSuccessful = $manager->save($comment);
510
-		$this->assertTrue($saveSuccessful, 'Comment saving was not successful');
511
-		$this->assertNotEquals('', $comment->getId(), 'Comment ID should not be empty');
512
-		$this->assertNotEquals('0', $comment->getId(), 'Comment ID should not be string \'0\'');
513
-		$this->assertNotNull($comment->getCreationDateTime(), 'Comment creation date should not be null');
514
-
515
-		$loadedComment = $manager->get($comment->getId());
516
-		$this->assertSame($comment->getMessage(), $loadedComment->getMessage(), 'Comment message should match');
517
-		$this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $loadedComment->getCreationDateTime()->getTimestamp(), 'Comment creation date should match');
518
-		return $comment;
519
-	}
520
-
521
-	public function providerTestSave(): array {
522
-		return [
523
-			['very beautiful, I am impressed!', 'alice', 'comment', null]
524
-		];
525
-	}
526
-
527
-	public function testSaveUpdate(): void {
528
-		$manager = $this->getManager();
529
-		$comment = new Comment();
530
-		$comment
531
-			->setActor('users', 'alice')
532
-			->setObject('files', 'file64')
533
-			->setMessage('very beautiful, I am impressed!')
534
-			->setVerb('comment')
535
-			->setExpireDate(new \DateTime('+2 hours'));
536
-
537
-		$manager->save($comment);
538
-
539
-		$loadedComment = $manager->get($comment->getId());
540
-		// Compare current object with database values
541
-		$this->assertSame($comment->getMessage(), $loadedComment->getMessage());
542
-		$this->assertSame(
543
-			$comment->getExpireDate()->format('Y-m-d H:i:s'),
544
-			$loadedComment->getExpireDate()->format('Y-m-d H:i:s')
545
-		);
546
-
547
-		// Preserve the original comment to compare after update
548
-		$original = clone $comment;
549
-
550
-		// Update values
551
-		$comment->setMessage('very beautiful, I am really so much impressed!')
552
-			->setExpireDate(new \DateTime('+1 hours'));
553
-		$manager->save($comment);
554
-
555
-		$loadedComment = $manager->get($comment->getId());
556
-		// Compare current object with database values
557
-		$this->assertSame($comment->getMessage(), $loadedComment->getMessage());
558
-		$this->assertSame(
559
-			$comment->getExpireDate()->format('Y-m-d H:i:s'),
560
-			$loadedComment->getExpireDate()->format('Y-m-d H:i:s')
561
-		);
562
-
563
-		// Compare original object with database values
564
-		$this->assertNotSame($original->getMessage(), $loadedComment->getMessage());
565
-		$this->assertNotSame(
566
-			$original->getExpireDate()->format('Y-m-d H:i:s'),
567
-			$loadedComment->getExpireDate()->format('Y-m-d H:i:s')
568
-		);
569
-	}
570
-
571
-
572
-	public function testSaveUpdateException(): void {
573
-		$this->expectException(\OCP\Comments\NotFoundException::class);
574
-
575
-		$manager = $this->getManager();
576
-		$comment = new Comment();
577
-		$comment
578
-			->setActor('users', 'alice')
579
-			->setObject('files', 'file64')
580
-			->setMessage('very beautiful, I am impressed!')
581
-			->setVerb('comment');
582
-
583
-		$manager->save($comment);
584
-
585
-		$manager->delete($comment->getId());
586
-		$comment->setMessage('very beautiful, I am really so much impressed!');
587
-		$manager->save($comment);
588
-	}
589
-
590
-
591
-	public function testSaveIncomplete(): void {
592
-		$this->expectException(\UnexpectedValueException::class);
593
-
594
-		$manager = $this->getManager();
595
-		$comment = new Comment();
596
-		$comment->setMessage('from no one to nothing');
597
-		$manager->save($comment);
598
-	}
599
-
600
-	public function testSaveAsChild(): void {
601
-		$id = $this->addDatabaseEntry(0, 0);
602
-
603
-		$manager = $this->getManager();
604
-
605
-		for ($i = 0; $i < 3; $i++) {
606
-			$comment = new Comment();
607
-			$comment
608
-				->setActor('users', 'alice')
609
-				->setObject('files', 'file64')
610
-				->setParentId(strval($id))
611
-				->setMessage('full ack')
612
-				->setVerb('comment')
613
-				// setting the creation time avoids using sleep() while making sure to test with different timestamps
614
-				->setCreationDateTime(new \DateTime('+' . $i . ' minutes'));
615
-
616
-			$manager->save($comment);
617
-
618
-			$this->assertSame($comment->getTopmostParentId(), strval($id));
619
-			$parentComment = $manager->get(strval($id));
620
-			$this->assertSame($parentComment->getChildrenCount(), $i + 1);
621
-			$this->assertEquals($parentComment->getLatestChildDateTime()->getTimestamp(), $comment->getCreationDateTime()->getTimestamp());
622
-		}
623
-	}
624
-
625
-	public function invalidActorArgsProvider() {
626
-		return
627
-			[
628
-				['', ''],
629
-				[1, 'alice'],
630
-				['users', 1],
631
-			];
632
-	}
633
-
634
-	/**
635
-	 * @dataProvider invalidActorArgsProvider
636
-	 * @param string $type
637
-	 * @param string $id
638
-	 */
639
-	public function testDeleteReferencesOfActorInvalidInput($type, $id): void {
640
-		$this->expectException(\InvalidArgumentException::class);
641
-
642
-		$manager = $this->getManager();
643
-		$manager->deleteReferencesOfActor($type, $id);
644
-	}
645
-
646
-	public function testDeleteReferencesOfActor(): void {
647
-		$ids = [];
648
-		$ids[] = $this->addDatabaseEntry(0, 0);
649
-		$ids[] = $this->addDatabaseEntry(0, 0);
650
-		$ids[] = $this->addDatabaseEntry(0, 0);
651
-
652
-		$manager = $this->getManager();
653
-
654
-		// just to make sure they are really set, with correct actor data
655
-		$comment = $manager->get(strval($ids[1]));
656
-		$this->assertSame($comment->getActorType(), 'users');
657
-		$this->assertSame($comment->getActorId(), 'alice');
658
-
659
-		$wasSuccessful = $manager->deleteReferencesOfActor('users', 'alice');
660
-		$this->assertTrue($wasSuccessful);
661
-
662
-		foreach ($ids as $id) {
663
-			$comment = $manager->get(strval($id));
664
-			$this->assertSame($comment->getActorType(), ICommentsManager::DELETED_USER);
665
-			$this->assertSame($comment->getActorId(), ICommentsManager::DELETED_USER);
666
-		}
667
-
668
-		// actor info is gone from DB, but when database interaction is alright,
669
-		// we still expect to get true back
670
-		$wasSuccessful = $manager->deleteReferencesOfActor('users', 'alice');
671
-		$this->assertTrue($wasSuccessful);
672
-	}
673
-
674
-	public function testDeleteReferencesOfActorWithUserManagement(): void {
675
-		$user = \OC::$server->getUserManager()->createUser('xenia', 'NotAnEasyPassword123456+');
676
-		$this->assertTrue($user instanceof IUser);
677
-
678
-		$manager = \OC::$server->get(ICommentsManager::class);
679
-		$comment = $manager->create('users', $user->getUID(), 'files', 'file64');
680
-		$comment
681
-			->setMessage('Most important comment I ever left on the Internet.')
682
-			->setVerb('comment');
683
-		$status = $manager->save($comment);
684
-		$this->assertTrue($status);
685
-
686
-		$commentID = $comment->getId();
687
-		$user->delete();
688
-
689
-		$comment = $manager->get($commentID);
690
-		$this->assertSame($comment->getActorType(), ICommentsManager::DELETED_USER);
691
-		$this->assertSame($comment->getActorId(), ICommentsManager::DELETED_USER);
692
-	}
693
-
694
-	public function invalidObjectArgsProvider() {
695
-		return
696
-			[
697
-				['', ''],
698
-				[1, 'file64'],
699
-				['files', 1],
700
-			];
701
-	}
702
-
703
-	/**
704
-	 * @dataProvider invalidObjectArgsProvider
705
-	 * @param string $type
706
-	 * @param string $id
707
-	 */
708
-	public function testDeleteCommentsAtObjectInvalidInput($type, $id): void {
709
-		$this->expectException(\InvalidArgumentException::class);
710
-
711
-		$manager = $this->getManager();
712
-		$manager->deleteCommentsAtObject($type, $id);
713
-	}
714
-
715
-	public function testDeleteCommentsAtObject(): void {
716
-		$ids = [];
717
-		$ids[] = $this->addDatabaseEntry(0, 0);
718
-		$ids[] = $this->addDatabaseEntry(0, 0);
719
-		$ids[] = $this->addDatabaseEntry(0, 0);
720
-
721
-		$manager = $this->getManager();
722
-
723
-		// just to make sure they are really set, with correct actor data
724
-		$comment = $manager->get(strval($ids[1]));
725
-		$this->assertSame($comment->getObjectType(), 'files');
726
-		$this->assertSame($comment->getObjectId(), 'file64');
727
-
728
-		$wasSuccessful = $manager->deleteCommentsAtObject('files', 'file64');
729
-		$this->assertTrue($wasSuccessful);
730
-
731
-		$verified = 0;
732
-		foreach ($ids as $id) {
733
-			try {
734
-				$manager->get(strval($id));
735
-			} catch (NotFoundException $e) {
736
-				$verified++;
737
-			}
738
-		}
739
-		$this->assertSame($verified, 3);
740
-
741
-		// actor info is gone from DB, but when database interaction is alright,
742
-		// we still expect to get true back
743
-		$wasSuccessful = $manager->deleteCommentsAtObject('files', 'file64');
744
-		$this->assertTrue($wasSuccessful);
745
-	}
746
-
747
-	public function testDeleteCommentsExpiredAtObjectTypeAndId(): void {
748
-		$ids = [];
749
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours'));
750
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours'));
751
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours'));
752
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours'));
753
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours'));
754
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours'));
755
-
756
-		$manager = new Manager(
757
-			$this->connection,
758
-			$this->createMock(LoggerInterface::class),
759
-			$this->createMock(IConfig::class),
760
-			Server::get(ITimeFactory::class),
761
-			new EmojiHelper($this->connection),
762
-			$this->createMock(IInitialStateService::class),
763
-			$this->rootFolder,
764
-			$this->createMock(IEventDispatcher::class)
765
-		);
766
-
767
-		// just to make sure they are really set, with correct actor data
768
-		$comment = $manager->get((string)$ids[1]);
769
-		$this->assertSame($comment->getObjectType(), 'files');
770
-		$this->assertSame($comment->getObjectId(), 'file64');
771
-
772
-		$deleted = $manager->deleteCommentsExpiredAtObject('files', 'file64');
773
-		$this->assertTrue($deleted);
774
-
775
-		$deleted = 0;
776
-		$exists = 0;
777
-		foreach ($ids as $id) {
778
-			try {
779
-				$manager->get((string)$id);
780
-				$exists++;
781
-			} catch (NotFoundException $e) {
782
-				$deleted++;
783
-			}
784
-		}
785
-		$this->assertSame($exists, 3);
786
-		$this->assertSame($deleted, 3);
787
-
788
-		// actor info is gone from DB, but when database interaction is alright,
789
-		// we still expect to get true back
790
-		$deleted = $manager->deleteCommentsExpiredAtObject('files', 'file64');
791
-		$this->assertFalse($deleted);
792
-	}
793
-
794
-	public function testDeleteCommentsExpiredAtObjectType(): void {
795
-		$ids = [];
796
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file1', new \DateTime('-2 hours'));
797
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file2', new \DateTime('-2 hours'));
798
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime('-2 hours'));
799
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime());
800
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime());
801
-		$ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime());
802
-
803
-		$manager = new Manager(
804
-			$this->connection,
805
-			$this->createMock(LoggerInterface::class),
806
-			$this->createMock(IConfig::class),
807
-			Server::get(ITimeFactory::class),
808
-			new EmojiHelper($this->connection),
809
-			$this->createMock(IInitialStateService::class),
810
-			$this->rootFolder,
811
-			$this->createMock(IEventDispatcher::class)
812
-		);
813
-
814
-		$deleted = $manager->deleteCommentsExpiredAtObject('files');
815
-		$this->assertTrue($deleted);
816
-
817
-		$deleted = 0;
818
-		$exists = 0;
819
-		foreach ($ids as $id) {
820
-			try {
821
-				$manager->get((string)$id);
822
-				$exists++;
823
-			} catch (NotFoundException $e) {
824
-				$deleted++;
825
-			}
826
-		}
827
-		$this->assertSame($exists, 0);
828
-		$this->assertSame($deleted, 6);
829
-
830
-		// actor info is gone from DB, but when database interaction is alright,
831
-		// we still expect to get true back
832
-		$deleted = $manager->deleteCommentsExpiredAtObject('files');
833
-		$this->assertFalse($deleted);
834
-	}
835
-
836
-	public function testSetMarkRead(): void {
837
-		/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
838
-		$user = $this->createMock(IUser::class);
839
-		$user->expects($this->any())
840
-			->method('getUID')
841
-			->willReturn('alice');
842
-
843
-		$dateTimeSet = new \DateTime();
844
-
845
-		$manager = $this->getManager();
846
-		$manager->setReadMark('robot', '36', $dateTimeSet, $user);
847
-
848
-		$dateTimeGet = $manager->getReadMark('robot', '36', $user);
849
-
850
-		$this->assertEquals($dateTimeGet->getTimestamp(), $dateTimeSet->getTimestamp());
851
-	}
852
-
853
-	public function testSetMarkReadUpdate(): void {
854
-		/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
855
-		$user = $this->createMock(IUser::class);
856
-		$user->expects($this->any())
857
-			->method('getUID')
858
-			->willReturn('alice');
859
-
860
-		$dateTimeSet = new \DateTime('yesterday');
861
-
862
-		$manager = $this->getManager();
863
-		$manager->setReadMark('robot', '36', $dateTimeSet, $user);
864
-
865
-		$dateTimeSet = new \DateTime('today');
866
-		$manager->setReadMark('robot', '36', $dateTimeSet, $user);
867
-
868
-		$dateTimeGet = $manager->getReadMark('robot', '36', $user);
869
-
870
-		$this->assertEquals($dateTimeGet, $dateTimeSet);
871
-	}
872
-
873
-	public function testReadMarkDeleteUser(): void {
874
-		/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
875
-		$user = $this->createMock(IUser::class);
876
-		$user->expects($this->any())
877
-			->method('getUID')
878
-			->willReturn('alice');
879
-
880
-		$dateTimeSet = new \DateTime();
881
-
882
-		$manager = $this->getManager();
883
-		$manager->setReadMark('robot', '36', $dateTimeSet, $user);
884
-
885
-		$manager->deleteReadMarksFromUser($user);
886
-		$dateTimeGet = $manager->getReadMark('robot', '36', $user);
887
-
888
-		$this->assertNull($dateTimeGet);
889
-	}
890
-
891
-	public function testReadMarkDeleteObject(): void {
892
-		/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
893
-		$user = $this->createMock(IUser::class);
894
-		$user->expects($this->any())
895
-			->method('getUID')
896
-			->willReturn('alice');
35
+    /** @var IDBConnection */
36
+    private $connection;
37
+    /** @var \PHPUnit\Framework\MockObject\MockObject|IRootFolder */
38
+    private $rootFolder;
39
+
40
+    protected function setUp(): void {
41
+        parent::setUp();
42
+
43
+        $this->connection = \OC::$server->getDatabaseConnection();
44
+        $this->rootFolder = $this->createMock(IRootFolder::class);
45
+
46
+        $sql = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*comments`');
47
+        $this->connection->prepare($sql)->execute();
48
+        $sql = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*reactions`');
49
+        $this->connection->prepare($sql)->execute();
50
+    }
51
+
52
+    protected function addDatabaseEntry($parentId, $topmostParentId, $creationDT = null, $latestChildDT = null, $objectId = null, $expireDate = null) {
53
+        if (is_null($creationDT)) {
54
+            $creationDT = new \DateTime();
55
+        }
56
+        if (is_null($latestChildDT)) {
57
+            $latestChildDT = new \DateTime('yesterday');
58
+        }
59
+        if (is_null($objectId)) {
60
+            $objectId = 'file64';
61
+        }
62
+
63
+        $qb = $this->connection->getQueryBuilder();
64
+        $qb
65
+            ->insert('comments')
66
+            ->values([
67
+                'parent_id' => $qb->createNamedParameter($parentId),
68
+                'topmost_parent_id' => $qb->createNamedParameter($topmostParentId),
69
+                'children_count' => $qb->createNamedParameter(2),
70
+                'actor_type' => $qb->createNamedParameter('users'),
71
+                'actor_id' => $qb->createNamedParameter('alice'),
72
+                'message' => $qb->createNamedParameter('nice one'),
73
+                'verb' => $qb->createNamedParameter('comment'),
74
+                'creation_timestamp' => $qb->createNamedParameter($creationDT, IQueryBuilder::PARAM_DATETIME_MUTABLE),
75
+                'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, IQueryBuilder::PARAM_DATETIME_MUTABLE),
76
+                'object_type' => $qb->createNamedParameter('files'),
77
+                'object_id' => $qb->createNamedParameter($objectId),
78
+                'expire_date' => $qb->createNamedParameter($expireDate, IQueryBuilder::PARAM_DATETIME_MUTABLE),
79
+                'reference_id' => $qb->createNamedParameter('referenceId'),
80
+                'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])),
81
+            ])
82
+            ->execute();
83
+
84
+        return $qb->getLastInsertId();
85
+    }
86
+
87
+    protected function getManager() {
88
+        return new Manager(
89
+            $this->connection,
90
+            $this->createMock(LoggerInterface::class),
91
+            $this->createMock(IConfig::class),
92
+            $this->createMock(ITimeFactory::class),
93
+            new EmojiHelper($this->connection),
94
+            $this->createMock(IInitialStateService::class),
95
+            $this->rootFolder,
96
+            $this->createMock(IEventDispatcher::class),
97
+        );
98
+    }
99
+
100
+
101
+    public function testGetCommentNotFound(): void {
102
+        $this->expectException(\OCP\Comments\NotFoundException::class);
103
+
104
+        $manager = $this->getManager();
105
+        $manager->get('22');
106
+    }
107
+
108
+
109
+    public function testGetCommentNotFoundInvalidInput(): void {
110
+        $this->expectException(\InvalidArgumentException::class);
111
+
112
+        $manager = $this->getManager();
113
+        $manager->get('unexisting22');
114
+    }
115
+
116
+    public function testGetComment(): void {
117
+        $manager = $this->getManager();
118
+
119
+        $creationDT = new \DateTime();
120
+        $latestChildDT = new \DateTime('yesterday');
121
+
122
+        $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
123
+        $qb
124
+            ->insert('comments')
125
+            ->values([
126
+                'parent_id' => $qb->createNamedParameter('2'),
127
+                'topmost_parent_id' => $qb->createNamedParameter('1'),
128
+                'children_count' => $qb->createNamedParameter(2),
129
+                'actor_type' => $qb->createNamedParameter('users'),
130
+                'actor_id' => $qb->createNamedParameter('alice'),
131
+                'message' => $qb->createNamedParameter('nice one'),
132
+                'verb' => $qb->createNamedParameter('comment'),
133
+                'creation_timestamp' => $qb->createNamedParameter($creationDT, 'datetime'),
134
+                'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, 'datetime'),
135
+                'object_type' => $qb->createNamedParameter('files'),
136
+                'object_id' => $qb->createNamedParameter('file64'),
137
+                'reference_id' => $qb->createNamedParameter('referenceId'),
138
+                'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])),
139
+            ])
140
+            ->execute();
141
+
142
+        $id = strval($qb->getLastInsertId());
143
+
144
+        $comment = $manager->get($id);
145
+        $this->assertTrue($comment instanceof IComment);
146
+        $this->assertSame($comment->getId(), $id);
147
+        $this->assertSame($comment->getParentId(), '2');
148
+        $this->assertSame($comment->getTopmostParentId(), '1');
149
+        $this->assertSame($comment->getChildrenCount(), 2);
150
+        $this->assertSame($comment->getActorType(), 'users');
151
+        $this->assertSame($comment->getActorId(), 'alice');
152
+        $this->assertSame($comment->getMessage(), 'nice one');
153
+        $this->assertSame($comment->getVerb(), 'comment');
154
+        $this->assertSame($comment->getObjectType(), 'files');
155
+        $this->assertSame($comment->getObjectId(), 'file64');
156
+        $this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $creationDT->getTimestamp());
157
+        $this->assertEquals($comment->getLatestChildDateTime(), $latestChildDT);
158
+        $this->assertEquals($comment->getReferenceId(), 'referenceId');
159
+        $this->assertEquals($comment->getMetaData(), ['last_edit_actor_id' => 'admin']);
160
+    }
161
+
162
+
163
+    public function testGetTreeNotFound(): void {
164
+        $this->expectException(\OCP\Comments\NotFoundException::class);
165
+
166
+        $manager = $this->getManager();
167
+        $manager->getTree('22');
168
+    }
169
+
170
+
171
+    public function testGetTreeNotFoundInvalidIpnut(): void {
172
+        $this->expectException(\InvalidArgumentException::class);
173
+
174
+        $manager = $this->getManager();
175
+        $manager->getTree('unexisting22');
176
+    }
177
+
178
+    public function testGetTree(): void {
179
+        $headId = $this->addDatabaseEntry(0, 0);
180
+
181
+        $this->addDatabaseEntry($headId, $headId, new \DateTime('-3 hours'));
182
+        $this->addDatabaseEntry($headId, $headId, new \DateTime('-2 hours'));
183
+        $id = $this->addDatabaseEntry($headId, $headId, new \DateTime('-1 hour'));
184
+
185
+        $manager = $this->getManager();
186
+        $tree = $manager->getTree($headId);
187
+
188
+        // Verifying the root comment
189
+        $this->assertTrue(isset($tree['comment']));
190
+        $this->assertTrue($tree['comment'] instanceof IComment);
191
+        $this->assertSame($tree['comment']->getId(), strval($headId));
192
+        $this->assertTrue(isset($tree['replies']));
193
+        $this->assertSame(count($tree['replies']), 3);
194
+
195
+        // one level deep
196
+        foreach ($tree['replies'] as $reply) {
197
+            $this->assertTrue($reply['comment'] instanceof IComment);
198
+            $this->assertSame($reply['comment']->getId(), strval($id));
199
+            $this->assertSame(count($reply['replies']), 0);
200
+            $id--;
201
+        }
202
+    }
203
+
204
+    public function testGetTreeNoReplies(): void {
205
+        $id = $this->addDatabaseEntry(0, 0);
206
+
207
+        $manager = $this->getManager();
208
+        $tree = $manager->getTree($id);
209
+
210
+        // Verifying the root comment
211
+        $this->assertTrue(isset($tree['comment']));
212
+        $this->assertTrue($tree['comment'] instanceof IComment);
213
+        $this->assertSame($tree['comment']->getId(), strval($id));
214
+        $this->assertTrue(isset($tree['replies']));
215
+        $this->assertSame(count($tree['replies']), 0);
216
+
217
+        // one level deep
218
+        foreach ($tree['replies'] as $reply) {
219
+            throw new \Exception('This ain`t happen');
220
+        }
221
+    }
222
+
223
+    public function testGetTreeWithLimitAndOffset(): void {
224
+        $headId = $this->addDatabaseEntry(0, 0);
225
+
226
+        $this->addDatabaseEntry($headId, $headId, new \DateTime('-3 hours'));
227
+        $this->addDatabaseEntry($headId, $headId, new \DateTime('-2 hours'));
228
+        $this->addDatabaseEntry($headId, $headId, new \DateTime('-1 hour'));
229
+        $idToVerify = $this->addDatabaseEntry($headId, $headId, new \DateTime());
230
+
231
+        $manager = $this->getManager();
232
+
233
+        for ($offset = 0; $offset < 3; $offset += 2) {
234
+            $tree = $manager->getTree(strval($headId), 2, $offset);
235
+
236
+            // Verifying the root comment
237
+            $this->assertTrue(isset($tree['comment']));
238
+            $this->assertTrue($tree['comment'] instanceof IComment);
239
+            $this->assertSame($tree['comment']->getId(), strval($headId));
240
+            $this->assertTrue(isset($tree['replies']));
241
+            $this->assertSame(count($tree['replies']), 2);
242
+
243
+            // one level deep
244
+            foreach ($tree['replies'] as $reply) {
245
+                $this->assertTrue($reply['comment'] instanceof IComment);
246
+                $this->assertSame($reply['comment']->getId(), strval($idToVerify));
247
+                $this->assertSame(count($reply['replies']), 0);
248
+                $idToVerify--;
249
+            }
250
+        }
251
+    }
252
+
253
+    public function testGetForObject(): void {
254
+        $this->addDatabaseEntry(0, 0);
255
+
256
+        $manager = $this->getManager();
257
+        $comments = $manager->getForObject('files', 'file64');
258
+
259
+        $this->assertTrue(is_array($comments));
260
+        $this->assertSame(count($comments), 1);
261
+        $this->assertTrue($comments[0] instanceof IComment);
262
+        $this->assertSame($comments[0]->getMessage(), 'nice one');
263
+    }
264
+
265
+    public function testGetForObjectWithLimitAndOffset(): void {
266
+        $this->addDatabaseEntry(0, 0, new \DateTime('-6 hours'));
267
+        $this->addDatabaseEntry(0, 0, new \DateTime('-5 hours'));
268
+        $this->addDatabaseEntry(1, 1, new \DateTime('-4 hours'));
269
+        $this->addDatabaseEntry(0, 0, new \DateTime('-3 hours'));
270
+        $this->addDatabaseEntry(2, 2, new \DateTime('-2 hours'));
271
+        $this->addDatabaseEntry(2, 2, new \DateTime('-1 hours'));
272
+        $idToVerify = $this->addDatabaseEntry(3, 1, new \DateTime());
273
+
274
+        $manager = $this->getManager();
275
+        $offset = 0;
276
+        do {
277
+            $comments = $manager->getForObject('files', 'file64', 3, $offset);
278
+
279
+            $this->assertTrue(is_array($comments));
280
+            foreach ($comments as $comment) {
281
+                $this->assertTrue($comment instanceof IComment);
282
+                $this->assertSame($comment->getMessage(), 'nice one');
283
+                $this->assertSame($comment->getId(), strval($idToVerify));
284
+                $idToVerify--;
285
+            }
286
+            $offset += 3;
287
+        } while (count($comments) > 0);
288
+    }
289
+
290
+    public function testGetForObjectWithDateTimeConstraint(): void {
291
+        $this->addDatabaseEntry(0, 0, new \DateTime('-6 hours'));
292
+        $this->addDatabaseEntry(0, 0, new \DateTime('-5 hours'));
293
+        $id1 = $this->addDatabaseEntry(0, 0, new \DateTime('-3 hours'));
294
+        $id2 = $this->addDatabaseEntry(2, 2, new \DateTime('-2 hours'));
295
+
296
+        $manager = $this->getManager();
297
+        $comments = $manager->getForObject('files', 'file64', 0, 0, new \DateTime('-4 hours'));
298
+
299
+        $this->assertSame(count($comments), 2);
300
+        $this->assertSame($comments[0]->getId(), strval($id2));
301
+        $this->assertSame($comments[1]->getId(), strval($id1));
302
+    }
303
+
304
+    public function testGetForObjectWithLimitAndOffsetAndDateTimeConstraint(): void {
305
+        $this->addDatabaseEntry(0, 0, new \DateTime('-7 hours'));
306
+        $this->addDatabaseEntry(0, 0, new \DateTime('-6 hours'));
307
+        $this->addDatabaseEntry(1, 1, new \DateTime('-5 hours'));
308
+        $this->addDatabaseEntry(0, 0, new \DateTime('-3 hours'));
309
+        $this->addDatabaseEntry(2, 2, new \DateTime('-2 hours'));
310
+        $this->addDatabaseEntry(2, 2, new \DateTime('-1 hours'));
311
+        $idToVerify = $this->addDatabaseEntry(3, 1, new \DateTime());
312
+
313
+        $manager = $this->getManager();
314
+        $offset = 0;
315
+        do {
316
+            $comments = $manager->getForObject('files', 'file64', 3, $offset, new \DateTime('-4 hours'));
317
+
318
+            $this->assertTrue(is_array($comments));
319
+            foreach ($comments as $comment) {
320
+                $this->assertTrue($comment instanceof IComment);
321
+                $this->assertSame($comment->getMessage(), 'nice one');
322
+                $this->assertSame($comment->getId(), strval($idToVerify));
323
+                $this->assertTrue(intval($comment->getId()) >= 4);
324
+                $idToVerify--;
325
+            }
326
+            $offset += 3;
327
+        } while (count($comments) > 0);
328
+    }
329
+
330
+    public function testGetNumberOfCommentsForObject(): void {
331
+        for ($i = 1; $i < 5; $i++) {
332
+            $this->addDatabaseEntry(0, 0);
333
+        }
334
+
335
+        $manager = $this->getManager();
336
+
337
+        $amount = $manager->getNumberOfCommentsForObject('untype', '00');
338
+        $this->assertSame($amount, 0);
339
+
340
+        $amount = $manager->getNumberOfCommentsForObject('files', 'file64');
341
+        $this->assertSame($amount, 4);
342
+    }
343
+
344
+    public function testGetNumberOfUnreadCommentsForFolder(): void {
345
+        $folder = $this->createMock(Folder::class);
346
+        $fileIds = range(1111, 1114);
347
+        $children = array_map(function (int $id) {
348
+            $file = $this->createMock(Folder::class);
349
+            $file->method('getId')
350
+                ->willReturn($id);
351
+            return $file;
352
+        }, $fileIds);
353
+        $folder->method('getId')->willReturn(1000);
354
+        $folder->method('getDirectoryListing')->willReturn($children);
355
+        $this->rootFolder->method('getFirstNodeById')
356
+            ->with($folder->getId())
357
+            ->willReturn($folder);
358
+
359
+        // 2 comment for 1111 with 1 before read marker
360
+        // 2 comments for 1112 with no read marker
361
+        // 1 comment for 1113 before read marker
362
+        // 1 comment for 1114 with no read marker
363
+        $this->addDatabaseEntry(0, 0, null, null, $fileIds[1]);
364
+        for ($i = 0; $i < 4; $i++) {
365
+            $this->addDatabaseEntry(0, 0, null, null, $fileIds[$i]);
366
+        }
367
+        $this->addDatabaseEntry(0, 0, (new \DateTime())->modify('-2 days'), null, $fileIds[0]);
368
+        /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
369
+        $user = $this->createMock(IUser::class);
370
+        $user->expects($this->any())
371
+            ->method('getUID')
372
+            ->willReturn('comment_test');
373
+
374
+        $manager = $this->getManager();
375
+
376
+        $manager->setReadMark('files', (string)$fileIds[0], (new \DateTime())->modify('-1 days'), $user);
377
+        $manager->setReadMark('files', (string)$fileIds[2], (new \DateTime()), $user);
378
+
379
+        $amount = $manager->getNumberOfUnreadCommentsForFolder($folder->getId(), $user);
380
+        $this->assertEquals([
381
+            $fileIds[0] => 1,
382
+            $fileIds[1] => 2,
383
+            $fileIds[3] => 1,
384
+        ], $amount);
385
+    }
386
+
387
+    /**
388
+     * @dataProvider dataGetForObjectSince
389
+     * @param $lastKnown
390
+     * @param $order
391
+     * @param $limit
392
+     * @param $resultFrom
393
+     * @param $resultTo
394
+     */
395
+    public function testGetForObjectSince($lastKnown, $order, $limit, $resultFrom, $resultTo): void {
396
+        $ids = [];
397
+        $ids[] = $this->addDatabaseEntry(0, 0);
398
+        $ids[] = $this->addDatabaseEntry(0, 0);
399
+        $ids[] = $this->addDatabaseEntry(0, 0);
400
+        $ids[] = $this->addDatabaseEntry(0, 0);
401
+        $ids[] = $this->addDatabaseEntry(0, 0);
402
+
403
+        $manager = $this->getManager();
404
+        $comments = $manager->getForObjectSince('files', 'file64', ($lastKnown === null ? 0 : $ids[$lastKnown]), $order, $limit);
405
+
406
+        $expected = array_slice($ids, $resultFrom, $resultTo - $resultFrom + 1);
407
+        if ($order === 'desc') {
408
+            $expected = array_reverse($expected);
409
+        }
410
+
411
+        $this->assertSame($expected, array_map(function (IComment $c) {
412
+            return (int)$c->getId();
413
+        }, $comments));
414
+    }
415
+
416
+    public function dataGetForObjectSince() {
417
+        return [
418
+            [null, 'asc', 20, 0, 4],
419
+            [null, 'asc', 2, 0, 1],
420
+            [null, 'desc', 20, 0, 4],
421
+            [null, 'desc', 2, 3, 4],
422
+            [1, 'asc', 20, 2, 4],
423
+            [1, 'asc', 2, 2, 3],
424
+            [3, 'desc', 20, 0, 2],
425
+            [3, 'desc', 2, 1, 2],
426
+        ];
427
+    }
428
+
429
+    public function invalidCreateArgsProvider() {
430
+        return [
431
+            ['', 'aId-1', 'oType-1', 'oId-1'],
432
+            ['aType-1', '', 'oType-1', 'oId-1'],
433
+            ['aType-1', 'aId-1', '', 'oId-1'],
434
+            ['aType-1', 'aId-1', 'oType-1', ''],
435
+            [1, 'aId-1', 'oType-1', 'oId-1'],
436
+            ['aType-1', 1, 'oType-1', 'oId-1'],
437
+            ['aType-1', 'aId-1', 1, 'oId-1'],
438
+            ['aType-1', 'aId-1', 'oType-1', 1],
439
+        ];
440
+    }
441
+
442
+    /**
443
+     * @dataProvider invalidCreateArgsProvider
444
+     * @param string $aType
445
+     * @param string $aId
446
+     * @param string $oType
447
+     * @param string $oId
448
+     */
449
+    public function testCreateCommentInvalidArguments($aType, $aId, $oType, $oId): void {
450
+        $this->expectException(\InvalidArgumentException::class);
451
+
452
+        $manager = $this->getManager();
453
+        $manager->create($aType, $aId, $oType, $oId);
454
+    }
455
+
456
+    public function testCreateComment(): void {
457
+        $actorType = 'bot';
458
+        $actorId = 'bob';
459
+        $objectType = 'weather';
460
+        $objectId = 'bielefeld';
461
+
462
+        $comment = $this->getManager()->create($actorType, $actorId, $objectType, $objectId);
463
+        $this->assertTrue($comment instanceof IComment);
464
+        $this->assertSame($comment->getActorType(), $actorType);
465
+        $this->assertSame($comment->getActorId(), $actorId);
466
+        $this->assertSame($comment->getObjectType(), $objectType);
467
+        $this->assertSame($comment->getObjectId(), $objectId);
468
+    }
469
+
470
+
471
+    public function testDelete(): void {
472
+        $this->expectException(\OCP\Comments\NotFoundException::class);
473
+
474
+        $manager = $this->getManager();
475
+
476
+        $done = $manager->delete('404');
477
+        $this->assertFalse($done);
478
+
479
+        $done = $manager->delete('%');
480
+        $this->assertFalse($done);
481
+
482
+        $done = $manager->delete('');
483
+        $this->assertFalse($done);
484
+
485
+        $id = strval($this->addDatabaseEntry(0, 0));
486
+        $comment = $manager->get($id);
487
+        $this->assertTrue($comment instanceof IComment);
488
+        $done = $manager->delete($id);
489
+        $this->assertTrue($done);
490
+        $manager->get($id);
491
+    }
492
+
493
+    /**
494
+     * @dataProvider providerTestSave
495
+     */
496
+    public function testSave(string $message, string $actorId, string $verb, ?string $parentId, ?string $id = ''): IComment {
497
+        $manager = $this->getManager();
498
+        $comment = new Comment();
499
+        $comment
500
+            ->setId($id)
501
+            ->setActor('users', $actorId)
502
+            ->setObject('files', 'file64')
503
+            ->setMessage($message)
504
+            ->setVerb($verb);
505
+        if ($parentId) {
506
+            $comment->setParentId($parentId);
507
+        }
508
+
509
+        $saveSuccessful = $manager->save($comment);
510
+        $this->assertTrue($saveSuccessful, 'Comment saving was not successful');
511
+        $this->assertNotEquals('', $comment->getId(), 'Comment ID should not be empty');
512
+        $this->assertNotEquals('0', $comment->getId(), 'Comment ID should not be string \'0\'');
513
+        $this->assertNotNull($comment->getCreationDateTime(), 'Comment creation date should not be null');
514
+
515
+        $loadedComment = $manager->get($comment->getId());
516
+        $this->assertSame($comment->getMessage(), $loadedComment->getMessage(), 'Comment message should match');
517
+        $this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $loadedComment->getCreationDateTime()->getTimestamp(), 'Comment creation date should match');
518
+        return $comment;
519
+    }
520
+
521
+    public function providerTestSave(): array {
522
+        return [
523
+            ['very beautiful, I am impressed!', 'alice', 'comment', null]
524
+        ];
525
+    }
526
+
527
+    public function testSaveUpdate(): void {
528
+        $manager = $this->getManager();
529
+        $comment = new Comment();
530
+        $comment
531
+            ->setActor('users', 'alice')
532
+            ->setObject('files', 'file64')
533
+            ->setMessage('very beautiful, I am impressed!')
534
+            ->setVerb('comment')
535
+            ->setExpireDate(new \DateTime('+2 hours'));
536
+
537
+        $manager->save($comment);
538
+
539
+        $loadedComment = $manager->get($comment->getId());
540
+        // Compare current object with database values
541
+        $this->assertSame($comment->getMessage(), $loadedComment->getMessage());
542
+        $this->assertSame(
543
+            $comment->getExpireDate()->format('Y-m-d H:i:s'),
544
+            $loadedComment->getExpireDate()->format('Y-m-d H:i:s')
545
+        );
546
+
547
+        // Preserve the original comment to compare after update
548
+        $original = clone $comment;
549
+
550
+        // Update values
551
+        $comment->setMessage('very beautiful, I am really so much impressed!')
552
+            ->setExpireDate(new \DateTime('+1 hours'));
553
+        $manager->save($comment);
554
+
555
+        $loadedComment = $manager->get($comment->getId());
556
+        // Compare current object with database values
557
+        $this->assertSame($comment->getMessage(), $loadedComment->getMessage());
558
+        $this->assertSame(
559
+            $comment->getExpireDate()->format('Y-m-d H:i:s'),
560
+            $loadedComment->getExpireDate()->format('Y-m-d H:i:s')
561
+        );
562
+
563
+        // Compare original object with database values
564
+        $this->assertNotSame($original->getMessage(), $loadedComment->getMessage());
565
+        $this->assertNotSame(
566
+            $original->getExpireDate()->format('Y-m-d H:i:s'),
567
+            $loadedComment->getExpireDate()->format('Y-m-d H:i:s')
568
+        );
569
+    }
570
+
571
+
572
+    public function testSaveUpdateException(): void {
573
+        $this->expectException(\OCP\Comments\NotFoundException::class);
574
+
575
+        $manager = $this->getManager();
576
+        $comment = new Comment();
577
+        $comment
578
+            ->setActor('users', 'alice')
579
+            ->setObject('files', 'file64')
580
+            ->setMessage('very beautiful, I am impressed!')
581
+            ->setVerb('comment');
582
+
583
+        $manager->save($comment);
584
+
585
+        $manager->delete($comment->getId());
586
+        $comment->setMessage('very beautiful, I am really so much impressed!');
587
+        $manager->save($comment);
588
+    }
589
+
590
+
591
+    public function testSaveIncomplete(): void {
592
+        $this->expectException(\UnexpectedValueException::class);
593
+
594
+        $manager = $this->getManager();
595
+        $comment = new Comment();
596
+        $comment->setMessage('from no one to nothing');
597
+        $manager->save($comment);
598
+    }
599
+
600
+    public function testSaveAsChild(): void {
601
+        $id = $this->addDatabaseEntry(0, 0);
602
+
603
+        $manager = $this->getManager();
604
+
605
+        for ($i = 0; $i < 3; $i++) {
606
+            $comment = new Comment();
607
+            $comment
608
+                ->setActor('users', 'alice')
609
+                ->setObject('files', 'file64')
610
+                ->setParentId(strval($id))
611
+                ->setMessage('full ack')
612
+                ->setVerb('comment')
613
+                // setting the creation time avoids using sleep() while making sure to test with different timestamps
614
+                ->setCreationDateTime(new \DateTime('+' . $i . ' minutes'));
615
+
616
+            $manager->save($comment);
617
+
618
+            $this->assertSame($comment->getTopmostParentId(), strval($id));
619
+            $parentComment = $manager->get(strval($id));
620
+            $this->assertSame($parentComment->getChildrenCount(), $i + 1);
621
+            $this->assertEquals($parentComment->getLatestChildDateTime()->getTimestamp(), $comment->getCreationDateTime()->getTimestamp());
622
+        }
623
+    }
624
+
625
+    public function invalidActorArgsProvider() {
626
+        return
627
+            [
628
+                ['', ''],
629
+                [1, 'alice'],
630
+                ['users', 1],
631
+            ];
632
+    }
633
+
634
+    /**
635
+     * @dataProvider invalidActorArgsProvider
636
+     * @param string $type
637
+     * @param string $id
638
+     */
639
+    public function testDeleteReferencesOfActorInvalidInput($type, $id): void {
640
+        $this->expectException(\InvalidArgumentException::class);
641
+
642
+        $manager = $this->getManager();
643
+        $manager->deleteReferencesOfActor($type, $id);
644
+    }
645
+
646
+    public function testDeleteReferencesOfActor(): void {
647
+        $ids = [];
648
+        $ids[] = $this->addDatabaseEntry(0, 0);
649
+        $ids[] = $this->addDatabaseEntry(0, 0);
650
+        $ids[] = $this->addDatabaseEntry(0, 0);
651
+
652
+        $manager = $this->getManager();
653
+
654
+        // just to make sure they are really set, with correct actor data
655
+        $comment = $manager->get(strval($ids[1]));
656
+        $this->assertSame($comment->getActorType(), 'users');
657
+        $this->assertSame($comment->getActorId(), 'alice');
658
+
659
+        $wasSuccessful = $manager->deleteReferencesOfActor('users', 'alice');
660
+        $this->assertTrue($wasSuccessful);
661
+
662
+        foreach ($ids as $id) {
663
+            $comment = $manager->get(strval($id));
664
+            $this->assertSame($comment->getActorType(), ICommentsManager::DELETED_USER);
665
+            $this->assertSame($comment->getActorId(), ICommentsManager::DELETED_USER);
666
+        }
667
+
668
+        // actor info is gone from DB, but when database interaction is alright,
669
+        // we still expect to get true back
670
+        $wasSuccessful = $manager->deleteReferencesOfActor('users', 'alice');
671
+        $this->assertTrue($wasSuccessful);
672
+    }
673
+
674
+    public function testDeleteReferencesOfActorWithUserManagement(): void {
675
+        $user = \OC::$server->getUserManager()->createUser('xenia', 'NotAnEasyPassword123456+');
676
+        $this->assertTrue($user instanceof IUser);
677
+
678
+        $manager = \OC::$server->get(ICommentsManager::class);
679
+        $comment = $manager->create('users', $user->getUID(), 'files', 'file64');
680
+        $comment
681
+            ->setMessage('Most important comment I ever left on the Internet.')
682
+            ->setVerb('comment');
683
+        $status = $manager->save($comment);
684
+        $this->assertTrue($status);
685
+
686
+        $commentID = $comment->getId();
687
+        $user->delete();
688
+
689
+        $comment = $manager->get($commentID);
690
+        $this->assertSame($comment->getActorType(), ICommentsManager::DELETED_USER);
691
+        $this->assertSame($comment->getActorId(), ICommentsManager::DELETED_USER);
692
+    }
693
+
694
+    public function invalidObjectArgsProvider() {
695
+        return
696
+            [
697
+                ['', ''],
698
+                [1, 'file64'],
699
+                ['files', 1],
700
+            ];
701
+    }
702
+
703
+    /**
704
+     * @dataProvider invalidObjectArgsProvider
705
+     * @param string $type
706
+     * @param string $id
707
+     */
708
+    public function testDeleteCommentsAtObjectInvalidInput($type, $id): void {
709
+        $this->expectException(\InvalidArgumentException::class);
710
+
711
+        $manager = $this->getManager();
712
+        $manager->deleteCommentsAtObject($type, $id);
713
+    }
714
+
715
+    public function testDeleteCommentsAtObject(): void {
716
+        $ids = [];
717
+        $ids[] = $this->addDatabaseEntry(0, 0);
718
+        $ids[] = $this->addDatabaseEntry(0, 0);
719
+        $ids[] = $this->addDatabaseEntry(0, 0);
720
+
721
+        $manager = $this->getManager();
722
+
723
+        // just to make sure they are really set, with correct actor data
724
+        $comment = $manager->get(strval($ids[1]));
725
+        $this->assertSame($comment->getObjectType(), 'files');
726
+        $this->assertSame($comment->getObjectId(), 'file64');
727
+
728
+        $wasSuccessful = $manager->deleteCommentsAtObject('files', 'file64');
729
+        $this->assertTrue($wasSuccessful);
730
+
731
+        $verified = 0;
732
+        foreach ($ids as $id) {
733
+            try {
734
+                $manager->get(strval($id));
735
+            } catch (NotFoundException $e) {
736
+                $verified++;
737
+            }
738
+        }
739
+        $this->assertSame($verified, 3);
740
+
741
+        // actor info is gone from DB, but when database interaction is alright,
742
+        // we still expect to get true back
743
+        $wasSuccessful = $manager->deleteCommentsAtObject('files', 'file64');
744
+        $this->assertTrue($wasSuccessful);
745
+    }
746
+
747
+    public function testDeleteCommentsExpiredAtObjectTypeAndId(): void {
748
+        $ids = [];
749
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours'));
750
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours'));
751
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('+2 hours'));
752
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours'));
753
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours'));
754
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, null, new \DateTime('-2 hours'));
755
+
756
+        $manager = new Manager(
757
+            $this->connection,
758
+            $this->createMock(LoggerInterface::class),
759
+            $this->createMock(IConfig::class),
760
+            Server::get(ITimeFactory::class),
761
+            new EmojiHelper($this->connection),
762
+            $this->createMock(IInitialStateService::class),
763
+            $this->rootFolder,
764
+            $this->createMock(IEventDispatcher::class)
765
+        );
766
+
767
+        // just to make sure they are really set, with correct actor data
768
+        $comment = $manager->get((string)$ids[1]);
769
+        $this->assertSame($comment->getObjectType(), 'files');
770
+        $this->assertSame($comment->getObjectId(), 'file64');
771
+
772
+        $deleted = $manager->deleteCommentsExpiredAtObject('files', 'file64');
773
+        $this->assertTrue($deleted);
774
+
775
+        $deleted = 0;
776
+        $exists = 0;
777
+        foreach ($ids as $id) {
778
+            try {
779
+                $manager->get((string)$id);
780
+                $exists++;
781
+            } catch (NotFoundException $e) {
782
+                $deleted++;
783
+            }
784
+        }
785
+        $this->assertSame($exists, 3);
786
+        $this->assertSame($deleted, 3);
787
+
788
+        // actor info is gone from DB, but when database interaction is alright,
789
+        // we still expect to get true back
790
+        $deleted = $manager->deleteCommentsExpiredAtObject('files', 'file64');
791
+        $this->assertFalse($deleted);
792
+    }
793
+
794
+    public function testDeleteCommentsExpiredAtObjectType(): void {
795
+        $ids = [];
796
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file1', new \DateTime('-2 hours'));
797
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file2', new \DateTime('-2 hours'));
798
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime('-2 hours'));
799
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime());
800
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime());
801
+        $ids[] = $this->addDatabaseEntry(0, 0, null, null, 'file3', new \DateTime());
802
+
803
+        $manager = new Manager(
804
+            $this->connection,
805
+            $this->createMock(LoggerInterface::class),
806
+            $this->createMock(IConfig::class),
807
+            Server::get(ITimeFactory::class),
808
+            new EmojiHelper($this->connection),
809
+            $this->createMock(IInitialStateService::class),
810
+            $this->rootFolder,
811
+            $this->createMock(IEventDispatcher::class)
812
+        );
813
+
814
+        $deleted = $manager->deleteCommentsExpiredAtObject('files');
815
+        $this->assertTrue($deleted);
816
+
817
+        $deleted = 0;
818
+        $exists = 0;
819
+        foreach ($ids as $id) {
820
+            try {
821
+                $manager->get((string)$id);
822
+                $exists++;
823
+            } catch (NotFoundException $e) {
824
+                $deleted++;
825
+            }
826
+        }
827
+        $this->assertSame($exists, 0);
828
+        $this->assertSame($deleted, 6);
829
+
830
+        // actor info is gone from DB, but when database interaction is alright,
831
+        // we still expect to get true back
832
+        $deleted = $manager->deleteCommentsExpiredAtObject('files');
833
+        $this->assertFalse($deleted);
834
+    }
835
+
836
+    public function testSetMarkRead(): void {
837
+        /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
838
+        $user = $this->createMock(IUser::class);
839
+        $user->expects($this->any())
840
+            ->method('getUID')
841
+            ->willReturn('alice');
842
+
843
+        $dateTimeSet = new \DateTime();
844
+
845
+        $manager = $this->getManager();
846
+        $manager->setReadMark('robot', '36', $dateTimeSet, $user);
847
+
848
+        $dateTimeGet = $manager->getReadMark('robot', '36', $user);
849
+
850
+        $this->assertEquals($dateTimeGet->getTimestamp(), $dateTimeSet->getTimestamp());
851
+    }
852
+
853
+    public function testSetMarkReadUpdate(): void {
854
+        /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
855
+        $user = $this->createMock(IUser::class);
856
+        $user->expects($this->any())
857
+            ->method('getUID')
858
+            ->willReturn('alice');
859
+
860
+        $dateTimeSet = new \DateTime('yesterday');
861
+
862
+        $manager = $this->getManager();
863
+        $manager->setReadMark('robot', '36', $dateTimeSet, $user);
864
+
865
+        $dateTimeSet = new \DateTime('today');
866
+        $manager->setReadMark('robot', '36', $dateTimeSet, $user);
867
+
868
+        $dateTimeGet = $manager->getReadMark('robot', '36', $user);
869
+
870
+        $this->assertEquals($dateTimeGet, $dateTimeSet);
871
+    }
872
+
873
+    public function testReadMarkDeleteUser(): void {
874
+        /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
875
+        $user = $this->createMock(IUser::class);
876
+        $user->expects($this->any())
877
+            ->method('getUID')
878
+            ->willReturn('alice');
879
+
880
+        $dateTimeSet = new \DateTime();
881
+
882
+        $manager = $this->getManager();
883
+        $manager->setReadMark('robot', '36', $dateTimeSet, $user);
884
+
885
+        $manager->deleteReadMarksFromUser($user);
886
+        $dateTimeGet = $manager->getReadMark('robot', '36', $user);
887
+
888
+        $this->assertNull($dateTimeGet);
889
+    }
890
+
891
+    public function testReadMarkDeleteObject(): void {
892
+        /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
893
+        $user = $this->createMock(IUser::class);
894
+        $user->expects($this->any())
895
+            ->method('getUID')
896
+            ->willReturn('alice');
897 897
 
898
-		$dateTimeSet = new \DateTime();
898
+        $dateTimeSet = new \DateTime();
899 899
 
900
-		$manager = $this->getManager();
901
-		$manager->setReadMark('robot', '36', $dateTimeSet, $user);
900
+        $manager = $this->getManager();
901
+        $manager->setReadMark('robot', '36', $dateTimeSet, $user);
902 902
 
903
-		$manager->deleteReadMarksOnObject('robot', '36');
904
-		$dateTimeGet = $manager->getReadMark('robot', '36', $user);
903
+        $manager->deleteReadMarksOnObject('robot', '36');
904
+        $dateTimeGet = $manager->getReadMark('robot', '36', $user);
905 905
 
906
-		$this->assertNull($dateTimeGet);
907
-	}
906
+        $this->assertNull($dateTimeGet);
907
+    }
908 908
 
909
-	public function testSendEvent(): void {
910
-		$handler1 = $this->getMockBuilder(ICommentsEventHandler::class)->getMock();
911
-		$handler1->expects($this->exactly(4))
912
-			->method('handle');
909
+    public function testSendEvent(): void {
910
+        $handler1 = $this->getMockBuilder(ICommentsEventHandler::class)->getMock();
911
+        $handler1->expects($this->exactly(4))
912
+            ->method('handle');
913 913
 
914
-		$handler2 = $this->getMockBuilder(ICommentsEventHandler::class)->getMock();
915
-		$handler1->expects($this->exactly(4))
916
-			->method('handle');
914
+        $handler2 = $this->getMockBuilder(ICommentsEventHandler::class)->getMock();
915
+        $handler1->expects($this->exactly(4))
916
+            ->method('handle');
917 917
 
918
-		$manager = $this->getManager();
919
-		$manager->registerEventHandler(function () use ($handler1) {
920
-			return $handler1;
921
-		});
922
-		$manager->registerEventHandler(function () use ($handler2) {
923
-			return $handler2;
924
-		});
918
+        $manager = $this->getManager();
919
+        $manager->registerEventHandler(function () use ($handler1) {
920
+            return $handler1;
921
+        });
922
+        $manager->registerEventHandler(function () use ($handler2) {
923
+            return $handler2;
924
+        });
925 925
 
926
-		$comment = new Comment();
927
-		$comment
928
-			->setActor('users', 'alice')
929
-			->setObject('files', 'file64')
930
-			->setMessage('very beautiful, I am impressed!')
931
-			->setVerb('comment');
926
+        $comment = new Comment();
927
+        $comment
928
+            ->setActor('users', 'alice')
929
+            ->setObject('files', 'file64')
930
+            ->setMessage('very beautiful, I am impressed!')
931
+            ->setVerb('comment');
932 932
 
933
-		// Add event
934
-		$manager->save($comment);
933
+        // Add event
934
+        $manager->save($comment);
935 935
 
936
-		// Update event
937
-		$comment->setMessage('Different topic');
938
-		$manager->save($comment);
936
+        // Update event
937
+        $comment->setMessage('Different topic');
938
+        $manager->save($comment);
939 939
 
940
-		// Delete event
941
-		$manager->delete($comment->getId());
942
-	}
940
+        // Delete event
941
+        $manager->delete($comment->getId());
942
+    }
943 943
 
944
-	public function testResolveDisplayName(): void {
945
-		$manager = $this->getManager();
944
+    public function testResolveDisplayName(): void {
945
+        $manager = $this->getManager();
946 946
 
947
-		$planetClosure = function ($name) {
948
-			return ucfirst($name);
949
-		};
950
-
951
-		$galaxyClosure = function ($name) {
952
-			return strtoupper($name);
953
-		};
954
-
955
-		$manager->registerDisplayNameResolver('planet', $planetClosure);
956
-		$manager->registerDisplayNameResolver('galaxy', $galaxyClosure);
957
-
958
-		$this->assertSame('Neptune', $manager->resolveDisplayName('planet', 'neptune'));
959
-		$this->assertSame('SOMBRERO', $manager->resolveDisplayName('galaxy', 'sombrero'));
960
-	}
961
-
962
-
963
-	public function testRegisterResolverDuplicate(): void {
964
-		$this->expectException(\OutOfBoundsException::class);
965
-
966
-		$manager = $this->getManager();
967
-
968
-		$planetClosure = function ($name) {
969
-			return ucfirst($name);
970
-		};
971
-		$manager->registerDisplayNameResolver('planet', $planetClosure);
972
-		$manager->registerDisplayNameResolver('planet', $planetClosure);
973
-	}
974
-
975
-
976
-	public function testRegisterResolverInvalidType(): void {
977
-		$this->expectException(\InvalidArgumentException::class);
978
-
979
-		$manager = $this->getManager();
980
-
981
-		$planetClosure = function ($name) {
982
-			return ucfirst($name);
983
-		};
984
-		$manager->registerDisplayNameResolver(1337, $planetClosure);
985
-	}
986
-
987
-
988
-	public function testResolveDisplayNameUnregisteredType(): void {
989
-		$this->expectException(\OutOfBoundsException::class);
990
-
991
-		$manager = $this->getManager();
992
-
993
-		$planetClosure = function ($name) {
994
-			return ucfirst($name);
995
-		};
996
-
997
-		$manager->registerDisplayNameResolver('planet', $planetClosure);
998
-		$manager->resolveDisplayName('galaxy', 'sombrero');
999
-	}
1000
-
1001
-	public function testResolveDisplayNameDirtyResolver(): void {
1002
-		$manager = $this->getManager();
1003
-
1004
-		$planetClosure = function () {
1005
-			return null;
1006
-		};
1007
-
1008
-		$manager->registerDisplayNameResolver('planet', $planetClosure);
1009
-		$this->assertTrue(is_string($manager->resolveDisplayName('planet', 'neptune')));
1010
-	}
1011
-
1012
-	private function skipIfNotSupport4ByteUTF() {
1013
-		if (!$this->getManager()->supportReactions()) {
1014
-			$this->markTestSkipped('MySQL doesn\'t support 4 byte UTF-8');
1015
-		}
1016
-	}
1017
-
1018
-	/**
1019
-	 * @dataProvider providerTestReactionAddAndDelete
1020
-	 *
1021
-	 * @param IComment[] $comments
1022
-	 * @param array $reactionsExpected
1023
-	 * @return void
1024
-	 */
1025
-	public function testReactionAddAndDelete(array $comments, array $reactionsExpected): void {
1026
-		$this->skipIfNotSupport4ByteUTF();
1027
-		$manager = $this->getManager();
1028
-
1029
-		$processedComments = $this->proccessComments($comments);
1030
-		$comment = end($processedComments);
1031
-		if ($comment->getParentId()) {
1032
-			$parent = $manager->get($comment->getParentId());
1033
-			$this->assertEqualsCanonicalizing($reactionsExpected, $parent->getReactions());
1034
-		}
1035
-	}
1036
-
1037
-	public function providerTestReactionAddAndDelete(): array {
1038
-		return[
1039
-			[
1040
-				[
1041
-					['message', 'alice', 'comment', null],
1042
-				], [],
1043
-			],
1044
-			[
1045
-				[
1046
-					['message', 'alice', 'comment', null],
1047
-					['
Please login to merge, or discard this patch.