StatusServiceTest.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2020, Georg Ehrke
  5. *
  6. * @author Georg Ehrke <oc.list@georgehrke.com>
  7. * @author Joas Schilling <coding@schilljs.com>
  8. *
  9. * @license GNU AGPL version 3 or any later version
  10. *
  11. * This program is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Affero General Public License as
  13. * published by the Free Software Foundation, either version 3 of the
  14. * License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU Affero General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. *
  24. */
  25. namespace OCA\UserStatus\Tests\Service;
  26. use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
  27. use OC\DB\Exceptions\DbalException;
  28. use OCA\UserStatus\Db\UserStatus;
  29. use OCA\UserStatus\Db\UserStatusMapper;
  30. use OCA\UserStatus\Exception\InvalidClearAtException;
  31. use OCA\UserStatus\Exception\InvalidMessageIdException;
  32. use OCA\UserStatus\Exception\InvalidStatusIconException;
  33. use OCA\UserStatus\Exception\InvalidStatusTypeException;
  34. use OCA\UserStatus\Exception\StatusMessageTooLongException;
  35. use OCA\UserStatus\Service\PredefinedStatusService;
  36. use OCA\UserStatus\Service\StatusService;
  37. use OCP\AppFramework\Db\DoesNotExistException;
  38. use OCP\AppFramework\Utility\ITimeFactory;
  39. use OCP\DB\Exception;
  40. use OCP\IConfig;
  41. use OCP\IEmojiHelper;
  42. use OCP\IUserManager;
  43. use OCP\UserStatus\IUserStatus;
  44. use PHPUnit\Framework\MockObject\MockObject;
  45. use Test\TestCase;
  46. class StatusServiceTest extends TestCase {
  47. /** @var UserStatusMapper|MockObject */
  48. private $mapper;
  49. /** @var ITimeFactory|MockObject */
  50. private $timeFactory;
  51. /** @var PredefinedStatusService|MockObject */
  52. private $predefinedStatusService;
  53. /** @var IEmojiHelper|MockObject */
  54. private $emojiHelper;
  55. /** @var IConfig|MockObject */
  56. private $config;
  57. /** @var IUserManager|MockObject */
  58. private $userManager;
  59. private StatusService $service;
  60. protected function setUp(): void {
  61. parent::setUp();
  62. $this->mapper = $this->createMock(UserStatusMapper::class);
  63. $this->timeFactory = $this->createMock(ITimeFactory::class);
  64. $this->predefinedStatusService = $this->createMock(PredefinedStatusService::class);
  65. $this->emojiHelper = $this->createMock(IEmojiHelper::class);
  66. $this->userManager = $this->createMock(IUserManager::class);
  67. $this->config = $this->createMock(IConfig::class);
  68. $this->config->method('getAppValue')
  69. ->willReturnMap([
  70. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
  71. ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no']
  72. ]);
  73. $this->service = new StatusService($this->mapper,
  74. $this->timeFactory,
  75. $this->predefinedStatusService,
  76. $this->emojiHelper,
  77. $this->config,
  78. $this->userManager
  79. );
  80. }
  81. public function testFindAll(): void {
  82. $status1 = $this->createMock(UserStatus::class);
  83. $status2 = $this->createMock(UserStatus::class);
  84. $this->mapper->expects($this->once())
  85. ->method('findAll')
  86. ->with(20, 50)
  87. ->willReturn([$status1, $status2]);
  88. $this->assertEquals([
  89. $status1,
  90. $status2,
  91. ], $this->service->findAll(20, 50));
  92. }
  93. public function testFindAllRecentStatusChanges(): void {
  94. $status1 = $this->createMock(UserStatus::class);
  95. $status2 = $this->createMock(UserStatus::class);
  96. $this->mapper->expects($this->once())
  97. ->method('findAllRecent')
  98. ->with(20, 50)
  99. ->willReturn([$status1, $status2]);
  100. $this->assertEquals([
  101. $status1,
  102. $status2,
  103. ], $this->service->findAllRecentStatusChanges(20, 50));
  104. }
  105. public function testFindAllRecentStatusChangesNoEnumeration(): void {
  106. $status1 = $this->createMock(UserStatus::class);
  107. $status2 = $this->createMock(UserStatus::class);
  108. $this->mapper->method('findAllRecent')
  109. ->with(20, 50)
  110. ->willReturn([$status1, $status2]);
  111. // Rebuild $this->service with user enumeration turned off
  112. $this->config = $this->createMock(IConfig::class);
  113. $this->config->method('getAppValue')
  114. ->willReturnMap([
  115. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
  116. ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no']
  117. ]);
  118. $this->service = new StatusService($this->mapper,
  119. $this->timeFactory,
  120. $this->predefinedStatusService,
  121. $this->emojiHelper,
  122. $this->config,
  123. $this->userManager
  124. );
  125. $this->assertEquals([], $this->service->findAllRecentStatusChanges(20, 50));
  126. // Rebuild $this->service with user enumeration limited to common groups
  127. $this->config = $this->createMock(IConfig::class);
  128. $this->config->method('getAppValue')
  129. ->willReturnMap([
  130. ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
  131. ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes']
  132. ]);
  133. $this->service = new StatusService($this->mapper,
  134. $this->timeFactory,
  135. $this->predefinedStatusService,
  136. $this->emojiHelper,
  137. $this->config,
  138. $this->userManager
  139. );
  140. $this->assertEquals([], $this->service->findAllRecentStatusChanges(20, 50));
  141. }
  142. public function testFindByUserIdDoesNotExist(): void {
  143. $this->mapper->expects($this->once())
  144. ->method('findByUserId')
  145. ->with('john.doe')
  146. ->willThrowException(new DoesNotExistException(''));
  147. $this->expectException(DoesNotExistException::class);
  148. $this->service->findByUserId('john.doe');
  149. }
  150. public function testFindAllAddDefaultMessage(): void {
  151. $status = new UserStatus();
  152. $status->setMessageId('commuting');
  153. $this->predefinedStatusService->expects($this->once())
  154. ->method('getDefaultStatusById')
  155. ->with('commuting')
  156. ->willReturn([
  157. 'id' => 'commuting',
  158. 'icon' => '🚌',
  159. 'message' => 'Commuting',
  160. 'clearAt' => [
  161. 'type' => 'period',
  162. 'time' => 1800,
  163. ],
  164. ]);
  165. $this->mapper->expects($this->once())
  166. ->method('findByUserId')
  167. ->with('john.doe')
  168. ->willReturn($status);
  169. $this->assertEquals($status, $this->service->findByUserId('john.doe'));
  170. $this->assertEquals('🚌', $status->getCustomIcon());
  171. $this->assertEquals('Commuting', $status->getCustomMessage());
  172. }
  173. public function testFindAllClearStatus(): void {
  174. $status = new UserStatus();
  175. $status->setStatus('online');
  176. $status->setStatusTimestamp(1000);
  177. $status->setIsUserDefined(true);
  178. $this->timeFactory->method('getTime')
  179. ->willReturn(2600);
  180. $this->mapper->expects($this->once())
  181. ->method('findByUserId')
  182. ->with('john.doe')
  183. ->willReturn($status);
  184. $this->assertEquals($status, $this->service->findByUserId('john.doe'));
  185. $this->assertEquals('offline', $status->getStatus());
  186. $this->assertEquals(2600, $status->getStatusTimestamp());
  187. $this->assertFalse($status->getIsUserDefined());
  188. }
  189. public function testFindAllClearMessage(): void {
  190. $status = new UserStatus();
  191. $status->setClearAt(50);
  192. $status->setMessageId('commuting');
  193. $status->setStatusTimestamp(60);
  194. $this->timeFactory->method('getTime')
  195. ->willReturn(60);
  196. $this->predefinedStatusService->expects($this->never())
  197. ->method('getDefaultStatusById');
  198. $this->mapper->expects($this->once())
  199. ->method('findByUserId')
  200. ->with('john.doe')
  201. ->willReturn($status);
  202. $this->assertEquals($status, $this->service->findByUserId('john.doe'));
  203. $this->assertNull($status->getClearAt());
  204. $this->assertNull($status->getMessageId());
  205. }
  206. /**
  207. * @param string $userId
  208. * @param string $status
  209. * @param int|null $statusTimestamp
  210. * @param bool $isUserDefined
  211. * @param bool $expectExisting
  212. * @param bool $expectSuccess
  213. * @param bool $expectTimeFactory
  214. * @param bool $expectException
  215. * @param string|null $expectedExceptionClass
  216. * @param string|null $expectedExceptionMessage
  217. *
  218. * @dataProvider setStatusDataProvider
  219. */
  220. public function testSetStatus(string $userId,
  221. string $status,
  222. ?int $statusTimestamp,
  223. bool $isUserDefined,
  224. bool $expectExisting,
  225. bool $expectSuccess,
  226. bool $expectTimeFactory,
  227. bool $expectException,
  228. ?string $expectedExceptionClass,
  229. ?string $expectedExceptionMessage): void {
  230. $userStatus = new UserStatus();
  231. if ($expectExisting) {
  232. $userStatus->setId(42);
  233. $userStatus->setUserId($userId);
  234. $this->mapper->expects($this->once())
  235. ->method('findByUserId')
  236. ->with($userId)
  237. ->willReturn($userStatus);
  238. } else {
  239. $this->mapper->expects($this->once())
  240. ->method('findByUserId')
  241. ->with($userId)
  242. ->willThrowException(new DoesNotExistException(''));
  243. }
  244. if ($expectTimeFactory) {
  245. $this->timeFactory
  246. ->method('getTime')
  247. ->willReturn(40);
  248. }
  249. if ($expectException) {
  250. $this->expectException($expectedExceptionClass);
  251. $this->expectExceptionMessage($expectedExceptionMessage);
  252. $this->service->setStatus($userId, $status, $statusTimestamp, $isUserDefined);
  253. }
  254. if ($expectSuccess) {
  255. if ($expectExisting) {
  256. $this->mapper->expects($this->once())
  257. ->method('update')
  258. ->willReturnArgument(0);
  259. } else {
  260. $this->mapper->expects($this->once())
  261. ->method('insert')
  262. ->willReturnArgument(0);
  263. }
  264. $actual = $this->service->setStatus($userId, $status, $statusTimestamp, $isUserDefined);
  265. $this->assertEquals('john.doe', $actual->getUserId());
  266. $this->assertEquals($status, $actual->getStatus());
  267. $this->assertEquals($statusTimestamp ?? 40, $actual->getStatusTimestamp());
  268. $this->assertEquals($isUserDefined, $actual->getIsUserDefined());
  269. }
  270. }
  271. public function setStatusDataProvider(): array {
  272. return [
  273. ['john.doe', 'online', 50, true, true, true, false, false, null, null],
  274. ['john.doe', 'online', 50, true, false, true, false, false, null, null],
  275. ['john.doe', 'online', 50, false, true, true, false, false, null, null],
  276. ['john.doe', 'online', 50, false, false, true, false, false, null, null],
  277. ['john.doe', 'online', null, true, true, true, true, false, null, null],
  278. ['john.doe', 'online', null, true, false, true, true, false, null, null],
  279. ['john.doe', 'online', null, false, true, true, true, false, null, null],
  280. ['john.doe', 'online', null, false, false, true, true, false, null, null],
  281. ['john.doe', 'away', 50, true, true, true, false, false, null, null],
  282. ['john.doe', 'away', 50, true, false, true, false, false, null, null],
  283. ['john.doe', 'away', 50, false, true, true, false, false, null, null],
  284. ['john.doe', 'away', 50, false, false, true, false, false, null, null],
  285. ['john.doe', 'away', null, true, true, true, true, false, null, null],
  286. ['john.doe', 'away', null, true, false, true, true, false, null, null],
  287. ['john.doe', 'away', null, false, true, true, true, false, null, null],
  288. ['john.doe', 'away', null, false, false, true, true, false, null, null],
  289. ['john.doe', 'dnd', 50, true, true, true, false, false, null, null],
  290. ['john.doe', 'dnd', 50, true, false, true, false, false, null, null],
  291. ['john.doe', 'dnd', 50, false, true, true, false, false, null, null],
  292. ['john.doe', 'dnd', 50, false, false, true, false, false, null, null],
  293. ['john.doe', 'dnd', null, true, true, true, true, false, null, null],
  294. ['john.doe', 'dnd', null, true, false, true, true, false, null, null],
  295. ['john.doe', 'dnd', null, false, true, true, true, false, null, null],
  296. ['john.doe', 'dnd', null, false, false, true, true, false, null, null],
  297. ['john.doe', 'invisible', 50, true, true, true, false, false, null, null],
  298. ['john.doe', 'invisible', 50, true, false, true, false, false, null, null],
  299. ['john.doe', 'invisible', 50, false, true, true, false, false, null, null],
  300. ['john.doe', 'invisible', 50, false, false, true, false, false, null, null],
  301. ['john.doe', 'invisible', null, true, true, true, true, false, null, null],
  302. ['john.doe', 'invisible', null, true, false, true, true, false, null, null],
  303. ['john.doe', 'invisible', null, false, true, true, true, false, null, null],
  304. ['john.doe', 'invisible', null, false, false, true, true, false, null, null],
  305. ['john.doe', 'offline', 50, true, true, true, false, false, null, null],
  306. ['john.doe', 'offline', 50, true, false, true, false, false, null, null],
  307. ['john.doe', 'offline', 50, false, true, true, false, false, null, null],
  308. ['john.doe', 'offline', 50, false, false, true, false, false, null, null],
  309. ['john.doe', 'offline', null, true, true, true, true, false, null, null],
  310. ['john.doe', 'offline', null, true, false, true, true, false, null, null],
  311. ['john.doe', 'offline', null, false, true, true, true, false, null, null],
  312. ['john.doe', 'offline', null, false, false, true, true, false, null, null],
  313. ['john.doe', 'illegal-status', 50, true, true, false, false, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
  314. ['john.doe', 'illegal-status', 50, true, false, false, false, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
  315. ['john.doe', 'illegal-status', 50, false, true, false, false, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
  316. ['john.doe', 'illegal-status', 50, false, false, false, false, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
  317. ['john.doe', 'illegal-status', null, true, true, false, true, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
  318. ['john.doe', 'illegal-status', null, true, false, false, true, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
  319. ['john.doe', 'illegal-status', null, false, true, false, true, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
  320. ['john.doe', 'illegal-status', null, false, false, false, true, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
  321. ];
  322. }
  323. /**
  324. * @param string $userId
  325. * @param string $messageId
  326. * @param bool $isValidMessageId
  327. * @param int|null $clearAt
  328. * @param bool $expectExisting
  329. * @param bool $expectSuccess
  330. * @param bool $expectException
  331. * @param string|null $expectedExceptionClass
  332. * @param string|null $expectedExceptionMessage
  333. *
  334. * @dataProvider setPredefinedMessageDataProvider
  335. */
  336. public function testSetPredefinedMessage(string $userId,
  337. string $messageId,
  338. bool $isValidMessageId,
  339. ?int $clearAt,
  340. bool $expectExisting,
  341. bool $expectSuccess,
  342. bool $expectException,
  343. ?string $expectedExceptionClass,
  344. ?string $expectedExceptionMessage): void {
  345. $userStatus = new UserStatus();
  346. if ($expectExisting) {
  347. $userStatus->setId(42);
  348. $userStatus->setUserId($userId);
  349. $userStatus->setStatus('offline');
  350. $userStatus->setStatusTimestamp(0);
  351. $userStatus->setIsUserDefined(false);
  352. $userStatus->setCustomIcon('😀');
  353. $userStatus->setCustomMessage('Foo');
  354. $this->mapper->expects($this->once())
  355. ->method('findByUserId')
  356. ->with($userId)
  357. ->willReturn($userStatus);
  358. } else {
  359. $this->mapper->expects($this->once())
  360. ->method('findByUserId')
  361. ->with($userId)
  362. ->willThrowException(new DoesNotExistException(''));
  363. }
  364. $this->predefinedStatusService->expects($this->once())
  365. ->method('isValidId')
  366. ->with($messageId)
  367. ->willReturn($isValidMessageId);
  368. $this->timeFactory
  369. ->method('getTime')
  370. ->willReturn(40);
  371. if ($expectException) {
  372. $this->expectException($expectedExceptionClass);
  373. $this->expectExceptionMessage($expectedExceptionMessage);
  374. $this->service->setPredefinedMessage($userId, $messageId, $clearAt);
  375. }
  376. if ($expectSuccess) {
  377. if ($expectExisting) {
  378. $this->mapper->expects($this->once())
  379. ->method('update')
  380. ->willReturnArgument(0);
  381. } else {
  382. $this->mapper->expects($this->once())
  383. ->method('insert')
  384. ->willReturnArgument(0);
  385. }
  386. $actual = $this->service->setPredefinedMessage($userId, $messageId, $clearAt);
  387. $this->assertEquals('john.doe', $actual->getUserId());
  388. $this->assertEquals('offline', $actual->getStatus());
  389. $this->assertEquals(0, $actual->getStatusTimestamp());
  390. $this->assertEquals(false, $actual->getIsUserDefined());
  391. $this->assertEquals($messageId, $actual->getMessageId());
  392. $this->assertNull($actual->getCustomIcon());
  393. $this->assertNull($actual->getCustomMessage());
  394. $this->assertEquals($clearAt, $actual->getClearAt());
  395. }
  396. }
  397. public function setPredefinedMessageDataProvider(): array {
  398. return [
  399. ['john.doe', 'sick-leave', true, null, true, true, false, null, null],
  400. ['john.doe', 'sick-leave', true, null, false, true, false, null, null],
  401. ['john.doe', 'sick-leave', true, 20, true, false, true, InvalidClearAtException::class, 'ClearAt is in the past'],
  402. ['john.doe', 'sick-leave', true, 20, false, false, true, InvalidClearAtException::class, 'ClearAt is in the past'],
  403. ['john.doe', 'sick-leave', true, 60, true, true, false, null, null],
  404. ['john.doe', 'sick-leave', true, 60, false, true, false, null, null],
  405. ['john.doe', 'illegal-message-id', false, null, true, false, true, InvalidMessageIdException::class, 'Message-Id "illegal-message-id" is not supported'],
  406. ['john.doe', 'illegal-message-id', false, null, false, false, true, InvalidMessageIdException::class, 'Message-Id "illegal-message-id" is not supported'],
  407. ];
  408. }
  409. /**
  410. * @param string $userId
  411. * @param string|null $statusIcon
  412. * @param bool $supportsEmoji
  413. * @param string $message
  414. * @param int|null $clearAt
  415. * @param bool $expectExisting
  416. * @param bool $expectSuccess
  417. * @param bool $expectException
  418. * @param string|null $expectedExceptionClass
  419. * @param string|null $expectedExceptionMessage
  420. *
  421. * @dataProvider setCustomMessageDataProvider
  422. */
  423. public function testSetCustomMessage(string $userId,
  424. ?string $statusIcon,
  425. bool $supportsEmoji,
  426. string $message,
  427. ?int $clearAt,
  428. bool $expectExisting,
  429. bool $expectSuccess,
  430. bool $expectException,
  431. ?string $expectedExceptionClass,
  432. ?string $expectedExceptionMessage): void {
  433. $userStatus = new UserStatus();
  434. if ($expectExisting) {
  435. $userStatus->setId(42);
  436. $userStatus->setUserId($userId);
  437. $userStatus->setStatus('offline');
  438. $userStatus->setStatusTimestamp(0);
  439. $userStatus->setIsUserDefined(false);
  440. $userStatus->setMessageId('messageId-42');
  441. $this->mapper->expects($this->once())
  442. ->method('findByUserId')
  443. ->with($userId)
  444. ->willReturn($userStatus);
  445. } else {
  446. $this->mapper->expects($this->once())
  447. ->method('findByUserId')
  448. ->with($userId)
  449. ->willThrowException(new DoesNotExistException(''));
  450. }
  451. $this->emojiHelper->method('isValidSingleEmoji')
  452. ->with($statusIcon)
  453. ->willReturn($supportsEmoji);
  454. $this->timeFactory
  455. ->method('getTime')
  456. ->willReturn(40);
  457. if ($expectException) {
  458. $this->expectException($expectedExceptionClass);
  459. $this->expectExceptionMessage($expectedExceptionMessage);
  460. $this->service->setCustomMessage($userId, $statusIcon, $message, $clearAt);
  461. }
  462. if ($expectSuccess) {
  463. if ($expectExisting) {
  464. $this->mapper->expects($this->once())
  465. ->method('update')
  466. ->willReturnArgument(0);
  467. } else {
  468. $this->mapper->expects($this->once())
  469. ->method('insert')
  470. ->willReturnArgument(0);
  471. }
  472. $actual = $this->service->setCustomMessage($userId, $statusIcon, $message, $clearAt);
  473. $this->assertEquals('john.doe', $actual->getUserId());
  474. $this->assertEquals('offline', $actual->getStatus());
  475. $this->assertEquals(0, $actual->getStatusTimestamp());
  476. $this->assertEquals(false, $actual->getIsUserDefined());
  477. $this->assertNull($actual->getMessageId());
  478. $this->assertEquals($statusIcon, $actual->getCustomIcon());
  479. $this->assertEquals($message, $actual->getCustomMessage());
  480. $this->assertEquals($clearAt, $actual->getClearAt());
  481. }
  482. }
  483. public function setCustomMessageDataProvider(): array {
  484. return [
  485. ['john.doe', '😁', true, 'Custom message', null, true, true, false, null, null],
  486. ['john.doe', '😁', true, 'Custom message', null, false, true, false, null, null],
  487. ['john.doe', null, false, 'Custom message', null, true, true, false, null, null],
  488. ['john.doe', null, false, 'Custom message', null, false, true, false, null, null],
  489. ['john.doe', '😁', false, 'Custom message', null, true, false, true, InvalidStatusIconException::class, 'Status-Icon is longer than one character'],
  490. ['john.doe', '😁', false, 'Custom message', null, false, false, true, InvalidStatusIconException::class, 'Status-Icon is longer than one character'],
  491. ['john.doe', null, false, 'Custom message that is way too long and violates the maximum length and hence should be rejected', null, true, false, true, StatusMessageTooLongException::class, 'Message is longer than supported length of 80 characters'],
  492. ['john.doe', null, false, 'Custom message that is way too long and violates the maximum length and hence should be rejected', null, false, false, true, StatusMessageTooLongException::class, 'Message is longer than supported length of 80 characters'],
  493. ['john.doe', '😁', true, 'Custom message', 80, true, true, false, null, null],
  494. ['john.doe', '😁', true, 'Custom message', 80, false, true, false, null, null],
  495. ['john.doe', '😁', true, 'Custom message', 20, true, false, true, InvalidClearAtException::class, 'ClearAt is in the past'],
  496. ['john.doe', '😁', true, 'Custom message', 20, false, false, true, InvalidClearAtException::class, 'ClearAt is in the past'],
  497. ];
  498. }
  499. public function testClearStatus(): void {
  500. $status = new UserStatus();
  501. $status->setId(1);
  502. $status->setUserId('john.doe');
  503. $status->setStatus('dnd');
  504. $status->setStatusTimestamp(1337);
  505. $status->setIsUserDefined(true);
  506. $status->setMessageId('messageId-42');
  507. $status->setCustomIcon('🙊');
  508. $status->setCustomMessage('My custom status message');
  509. $status->setClearAt(42);
  510. $this->mapper->expects($this->once())
  511. ->method('findByUserId')
  512. ->with('john.doe')
  513. ->willReturn($status);
  514. $this->mapper->expects($this->once())
  515. ->method('update')
  516. ->with($status);
  517. $actual = $this->service->clearStatus('john.doe');
  518. $this->assertTrue($actual);
  519. $this->assertEquals('offline', $status->getStatus());
  520. $this->assertEquals(0, $status->getStatusTimestamp());
  521. $this->assertFalse($status->getIsUserDefined());
  522. }
  523. public function testClearStatusDoesNotExist(): void {
  524. $this->mapper->expects($this->once())
  525. ->method('findByUserId')
  526. ->with('john.doe')
  527. ->willThrowException(new DoesNotExistException(''));
  528. $this->mapper->expects($this->never())
  529. ->method('update');
  530. $actual = $this->service->clearStatus('john.doe');
  531. $this->assertFalse($actual);
  532. }
  533. public function testClearMessage(): void {
  534. $status = new UserStatus();
  535. $status->setId(1);
  536. $status->setUserId('john.doe');
  537. $status->setStatus('dnd');
  538. $status->setStatusTimestamp(1337);
  539. $status->setIsUserDefined(true);
  540. $status->setMessageId('messageId-42');
  541. $status->setCustomIcon('🙊');
  542. $status->setCustomMessage('My custom status message');
  543. $status->setClearAt(42);
  544. $this->mapper->expects($this->once())
  545. ->method('findByUserId')
  546. ->with('john.doe')
  547. ->willReturn($status);
  548. $this->mapper->expects($this->once())
  549. ->method('update')
  550. ->with($status);
  551. $actual = $this->service->clearMessage('john.doe');
  552. $this->assertTrue($actual);
  553. $this->assertNull($status->getMessageId());
  554. $this->assertNull($status->getCustomMessage());
  555. $this->assertNull($status->getCustomIcon());
  556. $this->assertNull($status->getClearAt());
  557. }
  558. public function testClearMessageDoesNotExist(): void {
  559. $this->mapper->expects($this->once())
  560. ->method('findByUserId')
  561. ->with('john.doe')
  562. ->willThrowException(new DoesNotExistException(''));
  563. $this->mapper->expects($this->never())
  564. ->method('update');
  565. $actual = $this->service->clearMessage('john.doe');
  566. $this->assertFalse($actual);
  567. }
  568. public function testRemoveUserStatus(): void {
  569. $status = $this->createMock(UserStatus::class);
  570. $this->mapper->expects($this->once())
  571. ->method('findByUserId')
  572. ->with('john.doe')
  573. ->willReturn($status);
  574. $this->mapper->expects($this->once())
  575. ->method('delete')
  576. ->with($status);
  577. $actual = $this->service->removeUserStatus('john.doe');
  578. $this->assertTrue($actual);
  579. }
  580. public function testRemoveUserStatusDoesNotExist(): void {
  581. $this->mapper->expects($this->once())
  582. ->method('findByUserId')
  583. ->with('john.doe')
  584. ->willThrowException(new DoesNotExistException(''));
  585. $this->mapper->expects($this->never())
  586. ->method('delete');
  587. $actual = $this->service->removeUserStatus('john.doe');
  588. $this->assertFalse($actual);
  589. }
  590. public function testCleanStatusAutomaticOnline(): void {
  591. $status = new UserStatus();
  592. $status->setStatus(IUserStatus::ONLINE);
  593. $status->setStatusTimestamp(1337);
  594. $status->setIsUserDefined(false);
  595. $this->mapper->expects(self::once())
  596. ->method('update')
  597. ->with($status);
  598. parent::invokePrivate($this->service, 'cleanStatus', [$status]);
  599. }
  600. public function testCleanStatusCustomOffline(): void {
  601. $status = new UserStatus();
  602. $status->setStatus(IUserStatus::OFFLINE);
  603. $status->setStatusTimestamp(1337);
  604. $status->setIsUserDefined(true);
  605. $this->mapper->expects(self::once())
  606. ->method('update')
  607. ->with($status);
  608. parent::invokePrivate($this->service, 'cleanStatus', [$status]);
  609. }
  610. public function testCleanStatusCleanedAlready(): void {
  611. $status = new UserStatus();
  612. $status->setStatus(IUserStatus::OFFLINE);
  613. $status->setStatusTimestamp(1337);
  614. $status->setIsUserDefined(false);
  615. // Don't update the status again and again when no value changed
  616. $this->mapper->expects(self::never())
  617. ->method('update')
  618. ->with($status);
  619. parent::invokePrivate($this->service, 'cleanStatus', [$status]);
  620. }
  621. public function testBackupWorkingHasBackupAlready(): void {
  622. $p = $this->createMock(UniqueConstraintViolationException::class);
  623. $e = DbalException::wrap($p);
  624. $this->mapper->expects($this->once())
  625. ->method('createBackupStatus')
  626. ->with('john')
  627. ->willThrowException($e);
  628. $this->assertFalse($this->service->backupCurrentStatus('john'));
  629. }
  630. public function testBackupThrowsOther(): void {
  631. $e = new Exception('', Exception::REASON_CONNECTION_LOST);
  632. $this->mapper->expects($this->once())
  633. ->method('createBackupStatus')
  634. ->with('john')
  635. ->willThrowException($e);
  636. $this->expectException(Exception::class);
  637. $this->service->backupCurrentStatus('john');
  638. }
  639. public function testBackup(): void {
  640. $e = new Exception('', Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION);
  641. $this->mapper->expects($this->once())
  642. ->method('createBackupStatus')
  643. ->with('john')
  644. ->willReturn(true);
  645. $this->assertTrue($this->service->backupCurrentStatus('john'));
  646. }
  647. public function testRevertMultipleUserStatus(): void {
  648. $john = new UserStatus();
  649. $john->setId(1);
  650. $john->setStatus(IUserStatus::AWAY);
  651. $john->setStatusTimestamp(1337);
  652. $john->setIsUserDefined(false);
  653. $john->setMessageId('call');
  654. $john->setUserId('john');
  655. $john->setIsBackup(false);
  656. $johnBackup = new UserStatus();
  657. $johnBackup->setId(2);
  658. $johnBackup->setStatus(IUserStatus::ONLINE);
  659. $johnBackup->setStatusTimestamp(1337);
  660. $johnBackup->setIsUserDefined(true);
  661. $johnBackup->setMessageId('hello');
  662. $johnBackup->setUserId('_john');
  663. $johnBackup->setIsBackup(true);
  664. $noBackup = new UserStatus();
  665. $noBackup->setId(3);
  666. $noBackup->setStatus(IUserStatus::AWAY);
  667. $noBackup->setStatusTimestamp(1337);
  668. $noBackup->setIsUserDefined(false);
  669. $noBackup->setMessageId('call');
  670. $noBackup->setUserId('nobackup');
  671. $noBackup->setIsBackup(false);
  672. $backupOnly = new UserStatus();
  673. $backupOnly->setId(4);
  674. $backupOnly->setStatus(IUserStatus::ONLINE);
  675. $backupOnly->setStatusTimestamp(1337);
  676. $backupOnly->setIsUserDefined(true);
  677. $backupOnly->setMessageId('hello');
  678. $backupOnly->setUserId('_backuponly');
  679. $backupOnly->setIsBackup(true);
  680. $noBackupDND = new UserStatus();
  681. $noBackupDND->setId(5);
  682. $noBackupDND->setStatus(IUserStatus::DND);
  683. $noBackupDND->setStatusTimestamp(1337);
  684. $noBackupDND->setIsUserDefined(false);
  685. $noBackupDND->setMessageId('call');
  686. $noBackupDND->setUserId('nobackupanddnd');
  687. $noBackupDND->setIsBackup(false);
  688. $this->mapper->expects($this->once())
  689. ->method('findByUserIds')
  690. ->with(['john', 'nobackup', 'backuponly', 'nobackupanddnd', '_john', '_nobackup', '_backuponly', '_nobackupanddnd'])
  691. ->willReturn([
  692. $john,
  693. $johnBackup,
  694. $noBackup,
  695. $backupOnly,
  696. $noBackupDND,
  697. ]);
  698. $this->mapper->expects($this->once())
  699. ->method('deleteByIds')
  700. ->with([1, 3, 5]);
  701. $this->mapper->expects($this->once())
  702. ->method('restoreBackupStatuses')
  703. ->with([2]);
  704. $this->service->revertMultipleUserStatus(['john', 'nobackup', 'backuponly', 'nobackupanddnd'], 'call');
  705. }
  706. }