Files
deck/tests/unit/Activity/ActivityManagerTest.php
Carl Schwan 5cf486150a refactor: Fix psalm issues
- Add typing for most of the services, controllers and mappers
- Add api doc for mappers
- Use vendor-bin for psalm
- Use attributes for controllers
- Fix upload of attachments

Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2025-09-28 11:49:06 +02:00

499 lines
14 KiB
PHP

<?php
/**
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Deck\Activity;
use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\Assignment;
use OCA\Deck\Db\Attachment;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\Card;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\Label;
use OCA\Deck\Db\Stack;
use OCA\Deck\Db\StackMapper;
use OCA\Deck\Service\PermissionService;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\IL10N;
use OCP\IUser;
use OCP\L10N\IFactory;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class ActivityManagerTest extends TestCase {
/** @var ActivityManager */
private $activityManager;
/** @var IManager|MockObject */
private $manager;
/** @var PermissionService|MockObject */
private $permissionService;
/** @var BoardMapper|MockObject */
private $boardMapper;
/** @var CardMapper|MockObject */
private $cardMapper;
/** @var StackMapper|MockObject */
private $stackMapper;
/** @var AclMapper|MockObject */
private $aclMapper;
/** @var IFactory|MockObject */
private $l10nFactory;
/** @var IL10N|MockObject */
private $l10n;
/** @var string */
private $userId = 'admin';
public function setUp(): void {
$this->manager = $this->createMock(IManager::class);
$this->permissionService = $this->createMock(PermissionService::class);
$this->boardMapper = $this->createMock(BoardMapper::class);
$this->cardMapper = $this->createMock(CardMapper::class);
$this->stackMapper = $this->createMock(StackMapper::class);
$this->aclMapper = $this->createMock(AclMapper::class);
$this->l10nFactory = $this->createMock(IFactory::class);
$this->l10n = $this->createMock(IL10N::class);
$this->activityManager = new ActivityManager(
$this->manager,
$this->permissionService,
$this->boardMapper,
$this->cardMapper,
$this->stackMapper,
$this->aclMapper,
$this->l10nFactory,
$this->userId
);
}
public function testGetActivityFormatOwn() {
$managerClass = new \ReflectionClass(ActivityManager::class);
$this->l10n->expects(self::any())
->method('t')
->will($this->returnCallback(function ($s) {
return $s;
}));
$this->l10nFactory->method('get')
->with('deck', 'cz')
->willReturn($this->l10n);
foreach ($managerClass->getConstants() as $constant => $value) {
if (str_starts_with($constant, 'SUBJECT')) {
$format = $this->activityManager->getActivityFormat('cz', $value, [], false);
if ($format !== '') {
$this->assertStringContainsString('{user}', $format);
} else {
self::addWarning('No activity string found for ' . $constant);
}
$format = $this->activityManager->getActivityFormat('cz', $value, [], true);
if ($format !== '') {
$this->assertStringStartsWith('You', $format);
} else {
self::addWarning('No own activity string found for ' . $constant);
}
}
}
}
private function expectEventCreation($subject, $subjectParams) {
$event = $this->createMock(IEvent::class);
$this->manager->expects(self::once())
->method('generateEvent')
->willReturn($event);
$event->expects(self::once())->method('setApp')->willReturn($event);
$event->expects(self::once())->method('setType')->willReturn($event);
$event->expects(self::once())->method('setAuthor')->willReturn($event);
$event->expects(self::once())->method('setObject')->willReturn($event);
$event->expects(self::once())->method('setSubject')->with($subject, $subjectParams)->willReturn($event);
$event->expects(self::once())->method('setTimestamp')->willReturn($event);
return $event;
}
public function testCreateEvent() {
$board = new Board();
$board->setId(123);
$board->setTitle('');
$this->boardMapper->expects(self::once())
->method('find')
->willReturn($board);
$event = $this->expectEventCreation(ActivityManager::SUBJECT_BOARD_CREATE, [
'author' => 'admin'
]);
$actual = $this->invokePrivate($this->activityManager, 'createEvent', [
ActivityManager::DECK_OBJECT_BOARD,
$board,
ActivityManager::SUBJECT_BOARD_CREATE
]);
$this->assertEquals($event, $actual);
}
public function testCreateEventDescription() {
$board = new Board();
$board->setId(123);
$board->setTitle('');
$this->boardMapper->expects(self::once())
->method('find')
->willReturn($board);
$card = Card::fromRow([
'id' => 123,
'title' => 'My card',
'description' => str_repeat('A', 1000),
'stackId' => 42,
]);
$this->cardMapper->expects(self::any())
->method('find')
->willReturn($card);
$stack = Stack::fromRow([
'boardId' => 123,
]);
$this->stackMapper->expects(self::any())
->method('find')
->with(42)
->willReturn($stack);
$expectedCard = $card->jsonSerialize();
unset($expectedCard['description']);
$event = $this->expectEventCreation(ActivityManager::SUBJECT_CARD_UPDATE_DESCRIPTION, [
'card' => $expectedCard,
'stack' => $stack->jsonSerialize(),
'board' => $board->jsonSerialize(),
'diff' => true,
'author' => 'admin',
'after' => str_repeat('C', 2000),
]);
$actual = $this->invokePrivate($this->activityManager, 'createEvent', [
ActivityManager::DECK_OBJECT_CARD,
$card,
ActivityManager::SUBJECT_CARD_UPDATE_DESCRIPTION,
[
'before' => str_repeat('B', 2000),
'after' => str_repeat('C', 2000)
],
]);
$this->assertEquals($event, $actual);
}
public function testCreateEventLongDescription() {
$board = new Board();
$board->setId(123);
$board->setTitle('');
$this->boardMapper->expects(self::once())
->method('find')
->willReturn($board);
$card = new Card();
$card->setDescription(str_repeat('A', 5000));
$card->setTitle('My card');
$card->setId(123);
$card->setStackId(42);
$this->cardMapper->expects(self::any())
->method('find')
->willReturn($card);
$stack = Stack::fromRow([
'boardId' => 123,
]);
$this->stackMapper->expects(self::any())
->method('find')
->willReturn($stack);
$expectedCard = $card->jsonSerialize();
unset($expectedCard['description']);
$event = $this->expectEventCreation(ActivityManager::SUBJECT_CARD_UPDATE_DESCRIPTION, [
'card' => $expectedCard,
'stack' => $stack->jsonSerialize(),
'board' => $board->jsonSerialize(),
'diff' => true,
'author' => 'admin',
'after' => str_repeat('C', 2000) . '...',
]);
$actual = $this->invokePrivate($this->activityManager, 'createEvent', [
ActivityManager::DECK_OBJECT_CARD,
$card,
ActivityManager::SUBJECT_CARD_UPDATE_DESCRIPTION,
[
'before' => str_repeat('B', 5000),
'after' => str_repeat('C', 5000)
],
]);
$this->assertEquals($event, $actual);
}
public function testCreateEventLabel() {
$board = Board::fromRow([
'id' => 123,
'title' => 'My board'
]);
$this->boardMapper->expects(self::once())
->method('find')
->willReturn($board);
$card = Card::fromParams([]);
$card->setDescription(str_repeat('A', 5000));
$card->setTitle('My card');
$card->setId(123);
$card->setStackId(42);
$this->cardMapper->expects(self::any())
->method('find')
->willReturn($card);
$stack = Stack::fromRow([
'boardId' => 123,
]);
$this->stackMapper->expects(self::any())
->method('find')
->willReturn($stack);
$event = $this->expectEventCreation(ActivityManager::SUBJECT_CARD_UPDATE_TITLE, [
'card' => [
'id' => 123,
'title' => 'My card',
'archived' => false,
],
'stack' => $stack,
'board' => $board,
'author' => 'admin',
]);
$actual = $this->invokePrivate($this->activityManager, 'createEvent', [
ActivityManager::DECK_OBJECT_CARD,
$card,
ActivityManager::SUBJECT_CARD_UPDATE_TITLE
]);
$this->assertEquals($event, $actual);
}
public static function dataSendToUsers() {
return [
[ActivityManager::DECK_OBJECT_BOARD],
[ActivityManager::DECK_OBJECT_CARD],
];
}
private function mockUser($uid) {
$user = $this->createMock(IUser::class);
$user->expects(self::any())
->method('getUID')
->willReturn($uid);
return $user;
}
/**
* @dataProvider dataSendToUsers
*/
public function testSendToUser($objectType) {
$users = [
$this->mockUser('user1'),
$this->mockUser('user2'),
];
$event = $this->createMock(IEvent::class);
$event->expects(self::once())
->method('getObjectType')
->willReturn($objectType);
$event->expects(self::once())
->method('getObjectId')
->willReturn(1);
$event->expects(self::exactly(2))
->method('setAffectedUser')
->withConsecutive(
['user1'],
['user2'],
)
->willReturnSelf();
$mapper = null;
switch ($objectType) {
case ActivityManager::DECK_OBJECT_BOARD:
$mapper = $this->boardMapper;
break;
case ActivityManager::DECK_OBJECT_CARD:
$mapper = $this->cardMapper;
break;
}
$mapper->expects(self::once())
->method('findBoardId')
->willReturn(123);
$this->permissionService->expects(self::once())
->method('findUsers')
->willReturn($users);
$this->manager->expects(self::exactly(2))
->method('publish')
->with($event);
$this->invokePrivate($this->activityManager, 'sendToUsers', [$event]);
}
public static function dataFindObjectForEntity() {
$board = new Board();
$board->setId(1);
$stack = new Stack();
$stack->setBoardId(1);
$card = new Card();
$card->setId(3);
$attachment = new Attachment();
$attachment->setCardId(3);
$label = new Label();
$label->setCardId(3);
$label->setBoardId(1);
$assignedUser = new Assignment();
$assignedUser->setCardId(3);
return [
[ActivityManager::DECK_OBJECT_BOARD, $board],
[ActivityManager::DECK_OBJECT_BOARD, $stack],
[ActivityManager::DECK_OBJECT_BOARD, $label],
[ActivityManager::DECK_OBJECT_CARD, $card],
[ActivityManager::DECK_OBJECT_CARD, $attachment],
[ActivityManager::DECK_OBJECT_CARD, $assignedUser],
[ActivityManager::DECK_OBJECT_CARD, $label],
];
}
/**
* @param $objectType
* @param $entity
* @dataProvider dataFindObjectForEntity
*/
public function testFindObjectForEntity($objectType, $entity) {
$board = new Board();
$board->setId(1);
$card = new Card();
$card->setId(3);
$expected = null;
if ($objectType === ActivityManager::DECK_OBJECT_BOARD) {
$this->boardMapper->expects(self::once())
->method('find')
->with(1)
->willReturn($board);
$expected = $board;
}
if ($objectType === ActivityManager::DECK_OBJECT_CARD) {
$this->cardMapper->expects(self::once())
->method('find')
->with(3)
->willReturn($card);
$expected = $card;
}
$actual = $this->invokePrivate($this->activityManager, 'findObjectForEntity', [$objectType, $entity]);
$this->assertEquals($expected, $actual);
}
public function testFindDetailsForStack() {
$stack = new Stack();
$stack->setId(123);
$stack->setBoardId(999);
$board = new Board();
$board->setId(999);
$this->stackMapper->expects(self::once())
->method('find')
->with(123)
->willReturn($stack);
$this->boardMapper->expects(self::once())->method('find')
->with(999)
->willReturn($board);
$this->assertEquals([
'stack' => $stack,
'board' => $board
], $this->invokePrivate($this->activityManager, 'findDetailsForStack', [123]));
}
public function testFindDetailsForCard() {
$card = new Card();
$card->setId(555);
$card->setStackId(123);
$stack = new Stack();
$stack->setId(123);
$stack->setBoardId(999);
$board = new Board();
$board->setId(999);
$this->cardMapper->expects(self::once())
->method('find')
->with(555)
->willReturn($card);
$this->stackMapper->expects(self::once())
->method('find')
->with(123)
->willReturn($stack);
$this->boardMapper->expects(self::once())->method('find')
->with(999)
->willReturn($board);
$this->assertEquals([
'stack' => $stack,
'board' => $board,
'card' => [
'id' => $card->getId(),
'title' => $card->getTitle(),
'archived' => $card->getArchived()
]
], $this->invokePrivate($this->activityManager, 'findDetailsForCard', [555]));
}
public function testFindDetailsForAttachment() {
$attachment = new Attachment();
$attachment->setId(777);
$attachment->setCardId(555);
$card = new Card();
$card->setId(555);
$card->setStackId(123);
$stack = new Stack();
$stack->setId(123);
$stack->setBoardId(999);
$board = new Board();
$board->setId(999);
$this->cardMapper->expects(self::once())
->method('find')
->with(555)
->willReturn($card);
$this->stackMapper->expects(self::once())
->method('find')
->with(123)
->willReturn($stack);
$this->boardMapper->expects(self::once())->method('find')
->with(999)
->willReturn($board);
$this->assertEquals([
'stack' => $stack,
'board' => $board,
'card' => [
'id' => $card->getId(),
'title' => $card->getTitle(),
'archived' => $card->getArchived()
],
'attachment' => $attachment
], $this->invokePrivate($this->activityManager, 'findDetailsForAttachment', [$attachment]));
}
public function invokePrivate(&$object, $methodName, array $parameters = []) {
$reflection = new \ReflectionClass(get_class($object));
$method = $reflection->getMethod($methodName);
$method->setAccessible(true);
return $method->invokeArgs($object, $parameters);
}
}