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