mirror of
https://github.com/chenasraf/nextcloud-forum.git
synced 2026-05-18 01:28:58 +00:00
1226 lines
39 KiB
PHP
1226 lines
39 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace OCA\Forum\Tests\Controller;
|
|
|
|
use OCA\Forum\AppInfo\Application;
|
|
use OCA\Forum\Controller\ThreadController;
|
|
use OCA\Forum\Db\Category;
|
|
use OCA\Forum\Db\CategoryMapper;
|
|
use OCA\Forum\Db\ForumUser;
|
|
use OCA\Forum\Db\ForumUserMapper;
|
|
use OCA\Forum\Db\Post;
|
|
use OCA\Forum\Db\PostMapper;
|
|
use OCA\Forum\Db\Thread;
|
|
use OCA\Forum\Db\ThreadMapper;
|
|
use OCA\Forum\Db\ThreadSubscriptionMapper;
|
|
use OCA\Forum\Service\PermissionService;
|
|
use OCA\Forum\Service\ThreadEnrichmentService;
|
|
use OCA\Forum\Service\UserPreferencesService;
|
|
use OCA\Forum\Service\UserService;
|
|
use OCP\AppFramework\Db\DoesNotExistException;
|
|
use OCP\AppFramework\Http;
|
|
use OCP\IRequest;
|
|
use OCP\IUser;
|
|
use OCP\IUserSession;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class ThreadControllerTest extends TestCase {
|
|
private ThreadController $controller;
|
|
private ThreadMapper $threadMapper;
|
|
private CategoryMapper $categoryMapper;
|
|
private PostMapper $postMapper;
|
|
private ForumUserMapper $forumUserMapper;
|
|
private ThreadSubscriptionMapper $threadSubscriptionMapper;
|
|
private ThreadEnrichmentService $threadEnrichmentService;
|
|
private UserPreferencesService $userPreferencesService;
|
|
private UserService $userService;
|
|
private PermissionService $permissionService;
|
|
private IUserSession $userSession;
|
|
private LoggerInterface $logger;
|
|
private IRequest $request;
|
|
|
|
protected function setUp(): void {
|
|
$this->request = $this->createMock(IRequest::class);
|
|
$this->threadMapper = $this->createMock(ThreadMapper::class);
|
|
$this->categoryMapper = $this->createMock(CategoryMapper::class);
|
|
$this->postMapper = $this->createMock(PostMapper::class);
|
|
$this->forumUserMapper = $this->createMock(ForumUserMapper::class);
|
|
$this->threadSubscriptionMapper = $this->createMock(ThreadSubscriptionMapper::class);
|
|
$this->threadEnrichmentService = $this->createMock(ThreadEnrichmentService::class);
|
|
$this->userPreferencesService = $this->createMock(UserPreferencesService::class);
|
|
$this->userService = $this->createMock(UserService::class);
|
|
$this->permissionService = $this->createMock(PermissionService::class);
|
|
$this->userSession = $this->createMock(IUserSession::class);
|
|
$this->logger = $this->createMock(LoggerInterface::class);
|
|
|
|
// Mock thread enrichment service to return serialized thread with mock data
|
|
$this->threadEnrichmentService->method('enrichThread')
|
|
->willReturnCallback(function ($thread) {
|
|
$data = is_array($thread) ? $thread : $thread->jsonSerialize();
|
|
$data['author'] = ['userId' => $data['authorId'], 'displayName' => 'Test User'];
|
|
$data['categorySlug'] = 'test-category';
|
|
$data['categoryName'] = 'Test Category';
|
|
$data['isSubscribed'] = false;
|
|
return $data;
|
|
});
|
|
|
|
$this->controller = new ThreadController(
|
|
Application::APP_ID,
|
|
$this->request,
|
|
$this->threadMapper,
|
|
$this->categoryMapper,
|
|
$this->postMapper,
|
|
$this->forumUserMapper,
|
|
$this->threadSubscriptionMapper,
|
|
$this->threadEnrichmentService,
|
|
$this->userPreferencesService,
|
|
$this->userService,
|
|
$this->permissionService,
|
|
$this->userSession,
|
|
$this->logger
|
|
);
|
|
}
|
|
|
|
public function testIndexReturnsAllThreadsSuccessfully(): void {
|
|
$thread1 = $this->createMockThread(1, 1, 'user1', 'Test Thread 1');
|
|
$thread2 = $this->createMockThread(2, 1, 'user2', 'Test Thread 2');
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('findAll')
|
|
->willReturn([$thread1, $thread2]);
|
|
|
|
$this->userService->expects($this->once())
|
|
->method('enrichMultipleUsers')
|
|
->willReturn([
|
|
'user1' => ['userId' => 'user1', 'displayName' => 'User 1'],
|
|
'user2' => ['userId' => 'user2', 'displayName' => 'User 2'],
|
|
]);
|
|
|
|
$response = $this->controller->index();
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertIsArray($data);
|
|
$this->assertCount(2, $data);
|
|
}
|
|
|
|
public function testByCategoryReturnsThreadsSuccessfully(): void {
|
|
$categoryId = 1;
|
|
$limit = 50;
|
|
$offset = 0;
|
|
|
|
$thread1 = $this->createMockThread(1, $categoryId, 'user1', 'Test Thread 1');
|
|
$thread2 = $this->createMockThread(2, $categoryId, 'user2', 'Test Thread 2');
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('findByCategoryId')
|
|
->with($categoryId, $limit, $offset)
|
|
->willReturn([$thread1, $thread2]);
|
|
|
|
$this->userService->expects($this->once())
|
|
->method('enrichMultipleUsers')
|
|
->willReturn([
|
|
'user1' => ['userId' => 'user1', 'displayName' => 'User 1'],
|
|
'user2' => ['userId' => 'user2', 'displayName' => 'User 2'],
|
|
]);
|
|
|
|
$response = $this->controller->byCategory($categoryId, $limit, $offset);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertIsArray($data);
|
|
$this->assertCount(2, $data);
|
|
}
|
|
|
|
public function testShowReturnsThreadAndIncrementsViewCount(): void {
|
|
$threadId = 1;
|
|
$thread = $this->createMockThread($threadId, 1, 'user1', 'Test Thread');
|
|
$initialViewCount = $thread->getViewCount();
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedThread) use ($initialViewCount) {
|
|
$this->assertEquals($initialViewCount + 1, $updatedThread->getViewCount());
|
|
return $updatedThread;
|
|
});
|
|
|
|
$response = $this->controller->show($threadId);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertEquals($threadId, $data['id']);
|
|
}
|
|
|
|
public function testShowReturnsNotFoundWhenThreadDoesNotExist(): void {
|
|
$threadId = 999;
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willThrowException(new DoesNotExistException('Thread not found'));
|
|
|
|
$response = $this->controller->show($threadId);
|
|
|
|
$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
|
|
$this->assertEquals(['error' => 'Thread not found'], $response->getData());
|
|
}
|
|
|
|
public function testBySlugReturnsThreadAndIncrementsViewCount(): void {
|
|
$slug = 'test-thread';
|
|
$thread = $this->createMockThread(1, 1, 'user1', 'Test Thread');
|
|
$thread->setSlug($slug);
|
|
$initialViewCount = $thread->getViewCount();
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('findBySlug')
|
|
->with($slug)
|
|
->willReturn($thread);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedThread) use ($initialViewCount) {
|
|
$this->assertEquals($initialViewCount + 1, $updatedThread->getViewCount());
|
|
return $updatedThread;
|
|
});
|
|
|
|
$response = $this->controller->bySlug($slug);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertEquals($slug, $data['slug']);
|
|
}
|
|
|
|
public function testBySlugReturnsNotFoundWhenThreadDoesNotExist(): void {
|
|
$slug = 'non-existent-thread';
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('findBySlug')
|
|
->with($slug)
|
|
->willThrowException(new DoesNotExistException('Thread not found'));
|
|
|
|
$response = $this->controller->bySlug($slug);
|
|
|
|
$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
|
|
$this->assertEquals(['error' => 'Thread not found'], $response->getData());
|
|
}
|
|
|
|
public function testCreateThreadSuccessfully(): void {
|
|
$categoryId = 1;
|
|
$title = 'New Thread';
|
|
$content = 'This is the initial post content';
|
|
$userId = 'user1';
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($userId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$forumUser = new ForumUser();
|
|
$forumUser->setUserId($userId);
|
|
$forumUser->setPostCount(10);
|
|
|
|
// Mock forum user increment methods (first post doesn't count, only thread count increments)
|
|
$this->forumUserMapper->expects($this->never())
|
|
->method('incrementPostCount');
|
|
$this->forumUserMapper->expects($this->once())
|
|
->method('incrementThreadCount');
|
|
|
|
// Mock thread subscription
|
|
$this->userPreferencesService->method('getPreference')->willReturn(false);
|
|
|
|
$category = new Category();
|
|
$category->setId($categoryId);
|
|
$category->setThreadCount(5);
|
|
$category->setPostCount(20);
|
|
|
|
$createdThread = $this->createMockThread(1, $categoryId, $userId, $title);
|
|
$createdThread->setSlug('new-thread');
|
|
|
|
$createdPost = new Post();
|
|
$createdPost->setId(1);
|
|
$createdPost->setThreadId(1);
|
|
$createdPost->setAuthorId($userId);
|
|
$createdPost->setContent($content);
|
|
|
|
// Mock findBySlug to return DoesNotExistException (slug doesn't exist yet)
|
|
$this->threadMapper->expects($this->once())
|
|
->method('findBySlug')
|
|
->willThrowException(new DoesNotExistException('Thread not found'));
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('insert')
|
|
->willReturnCallback(function ($thread) use ($createdThread) {
|
|
return $createdThread;
|
|
});
|
|
|
|
$this->postMapper->expects($this->once())
|
|
->method('insert')
|
|
->willReturn($createdPost);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($thread) {
|
|
// First post doesn't count, so postCount should be 0
|
|
$this->assertEquals(0, $thread->getPostCount());
|
|
return $thread;
|
|
});
|
|
|
|
$this->categoryMapper->expects($this->once())
|
|
->method('find')
|
|
->with($categoryId)
|
|
->willReturn($category);
|
|
|
|
$this->categoryMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedCategory) {
|
|
$this->assertEquals(6, $updatedCategory->getThreadCount());
|
|
// First post doesn't count, so postCount stays at 20
|
|
$this->assertEquals(20, $updatedCategory->getPostCount());
|
|
return $updatedCategory;
|
|
});
|
|
|
|
$response = $this->controller->create($categoryId, $title, $content);
|
|
|
|
$this->assertEquals(Http::STATUS_CREATED, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertEquals(1, $data['id']);
|
|
$this->assertEquals($categoryId, $data['categoryId']);
|
|
$this->assertEquals($userId, $data['authorId']);
|
|
$this->assertEquals($title, $data['title']);
|
|
$this->assertNotEmpty($data['slug']);
|
|
$this->assertEquals('new-thread', $data['slug']);
|
|
}
|
|
|
|
public function testCreateThreadReturnsUnauthorizedWhenUserNotAuthenticated(): void {
|
|
$categoryId = 1;
|
|
$title = 'New Thread';
|
|
$content = 'Initial post content';
|
|
|
|
$this->userSession->method('getUser')->willReturn(null);
|
|
|
|
$response = $this->controller->create($categoryId, $title, $content);
|
|
|
|
$this->assertEquals(Http::STATUS_UNAUTHORIZED, $response->getStatus());
|
|
$this->assertEquals(['error' => 'User not authenticated'], $response->getData());
|
|
}
|
|
|
|
public function testCreateThreadSucceedsEvenWhenForumUserUpdateFails(): void {
|
|
$categoryId = 1;
|
|
$title = 'New Thread';
|
|
$content = 'Initial post content';
|
|
$userId = 'user1';
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($userId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
// Mock forum user methods to throw exceptions (simulating forum user update failure)
|
|
// The controller catches these and just logs warnings, so thread creation should still succeed
|
|
$this->forumUserMapper->method('incrementPostCount')
|
|
->willThrowException(new \Exception('Failed to increment post count'));
|
|
$this->forumUserMapper->method('incrementThreadCount')
|
|
->willThrowException(new \Exception('Failed to increment thread count'));
|
|
|
|
// Mock thread subscription
|
|
$this->userPreferencesService->method('getPreference')
|
|
->willReturn(false);
|
|
|
|
// Mock thread creation parts
|
|
$this->threadMapper->method('findBySlug')->willThrowException(new DoesNotExistException(''));
|
|
$thread = $this->createMockThread(1, $categoryId, $userId, $title);
|
|
$this->threadMapper->method('insert')->willReturn($thread);
|
|
$post = new Post();
|
|
$post->setId(1);
|
|
$this->postMapper->method('insert')->willReturn($post);
|
|
$this->threadMapper->method('update')->willReturn($thread);
|
|
$category = new Category();
|
|
$category->setThreadCount(0);
|
|
$category->setPostCount(0);
|
|
$this->categoryMapper->method('find')->willReturn($category);
|
|
$this->categoryMapper->method('update')->willReturn($category);
|
|
|
|
$response = $this->controller->create($categoryId, $title, $content);
|
|
|
|
// Thread creation should succeed even if forum user update fails (they're in a try-catch)
|
|
$this->assertEquals(Http::STATUS_CREATED, $response->getStatus());
|
|
}
|
|
|
|
public function testUpdateThreadTitleSuccessfullyAsAuthor(): void {
|
|
$threadId = 1;
|
|
$categoryId = 1;
|
|
$authorId = 'user1';
|
|
$newTitle = 'Updated Title';
|
|
$thread = $this->createMockThread($threadId, $categoryId, $authorId, 'Original Title');
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($authorId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->permissionService->expects($this->once())
|
|
->method('hasCategoryPermission')
|
|
->with($authorId, $categoryId, 'canModerate')
|
|
->willReturn(false);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedThread) use ($newTitle) {
|
|
$this->assertEquals($newTitle, $updatedThread->getTitle());
|
|
return $updatedThread;
|
|
});
|
|
|
|
$response = $this->controller->update($threadId, $newTitle);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertEquals($threadId, $data['id']);
|
|
}
|
|
|
|
public function testUpdateThreadTitleSuccessfullyAsModerator(): void {
|
|
$threadId = 1;
|
|
$categoryId = 1;
|
|
$authorId = 'user1';
|
|
$moderatorId = 'moderator1';
|
|
$newTitle = 'Updated Title';
|
|
$thread = $this->createMockThread($threadId, $categoryId, $authorId, 'Original Title');
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($moderatorId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->permissionService->expects($this->once())
|
|
->method('hasCategoryPermission')
|
|
->with($moderatorId, $categoryId, 'canModerate')
|
|
->willReturn(true);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedThread) use ($newTitle) {
|
|
$this->assertEquals($newTitle, $updatedThread->getTitle());
|
|
return $updatedThread;
|
|
});
|
|
|
|
$response = $this->controller->update($threadId, $newTitle);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertEquals($threadId, $data['id']);
|
|
}
|
|
|
|
public function testUpdateThreadTitleReturnsForbiddenWhenUserIsNeitherAuthorNorModerator(): void {
|
|
$threadId = 1;
|
|
$categoryId = 1;
|
|
$authorId = 'user1';
|
|
$otherUserId = 'user2';
|
|
$newTitle = 'Updated Title';
|
|
$thread = $this->createMockThread($threadId, $categoryId, $authorId, 'Original Title');
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($otherUserId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->permissionService->expects($this->once())
|
|
->method('hasCategoryPermission')
|
|
->with($otherUserId, $categoryId, 'canModerate')
|
|
->willReturn(false);
|
|
|
|
$this->threadMapper->expects($this->never())
|
|
->method('update');
|
|
|
|
$response = $this->controller->update($threadId, $newTitle);
|
|
|
|
$this->assertEquals(Http::STATUS_FORBIDDEN, $response->getStatus());
|
|
$this->assertEquals(['error' => 'You do not have permission to edit this thread title'], $response->getData());
|
|
}
|
|
|
|
public function testUpdateThreadLockedStatus(): void {
|
|
$threadId = 1;
|
|
$categoryId = 1;
|
|
$moderatorId = 'moderator1';
|
|
$thread = $this->createMockThread($threadId, $categoryId, 'user1', 'Test Thread');
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($moderatorId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->permissionService->expects($this->once())
|
|
->method('hasCategoryPermission')
|
|
->with($moderatorId, $categoryId, 'canModerate')
|
|
->willReturn(true);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedThread) {
|
|
$this->assertTrue($updatedThread->getIsLocked());
|
|
return $updatedThread;
|
|
});
|
|
|
|
$response = $this->controller->update($threadId, null, true);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
}
|
|
|
|
public function testUpdateThreadPinnedStatus(): void {
|
|
$threadId = 1;
|
|
$categoryId = 1;
|
|
$moderatorId = 'moderator1';
|
|
$thread = $this->createMockThread($threadId, $categoryId, 'user1', 'Test Thread');
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($moderatorId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->permissionService->expects($this->once())
|
|
->method('hasCategoryPermission')
|
|
->with($moderatorId, $categoryId, 'canModerate')
|
|
->willReturn(true);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedThread) {
|
|
$this->assertTrue($updatedThread->getIsPinned());
|
|
return $updatedThread;
|
|
});
|
|
|
|
$response = $this->controller->update($threadId, null, null, true);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
}
|
|
|
|
public function testUpdateThreadHiddenStatus(): void {
|
|
$threadId = 1;
|
|
$categoryId = 1;
|
|
$moderatorId = 'moderator1';
|
|
$thread = $this->createMockThread($threadId, $categoryId, 'user1', 'Test Thread');
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($moderatorId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->permissionService->expects($this->once())
|
|
->method('hasCategoryPermission')
|
|
->with($moderatorId, $categoryId, 'canModerate')
|
|
->willReturn(true);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedThread) {
|
|
$this->assertTrue($updatedThread->getIsHidden());
|
|
return $updatedThread;
|
|
});
|
|
|
|
$response = $this->controller->update($threadId, null, null, null, true);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
}
|
|
|
|
public function testUpdateThreadReturnsUnauthorizedWhenUserNotAuthenticated(): void {
|
|
$threadId = 1;
|
|
$newTitle = 'New Title';
|
|
|
|
$this->userSession->method('getUser')->willReturn(null);
|
|
|
|
$this->threadMapper->expects($this->never())
|
|
->method('find');
|
|
|
|
$response = $this->controller->update($threadId, $newTitle);
|
|
|
|
$this->assertEquals(Http::STATUS_UNAUTHORIZED, $response->getStatus());
|
|
$this->assertEquals(['error' => 'User not authenticated'], $response->getData());
|
|
}
|
|
|
|
public function testUpdateThreadReturnsNotFoundWhenThreadDoesNotExist(): void {
|
|
$threadId = 999;
|
|
$userId = 'user1';
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($userId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willThrowException(new DoesNotExistException('Thread not found'));
|
|
|
|
$response = $this->controller->update($threadId, 'New Title');
|
|
|
|
$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
|
|
$this->assertEquals(['error' => 'Thread not found'], $response->getData());
|
|
}
|
|
|
|
public function testUpdateThreadStatusReturnsForbiddenWhenUserLacksModeratePermission(): void {
|
|
$threadId = 1;
|
|
$categoryId = 1;
|
|
$userId = 'user1';
|
|
$thread = $this->createMockThread($threadId, $categoryId, $userId, 'Test Thread');
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($userId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->permissionService->expects($this->once())
|
|
->method('hasCategoryPermission')
|
|
->with($userId, $categoryId, 'canModerate')
|
|
->willReturn(false);
|
|
|
|
$this->threadMapper->expects($this->never())
|
|
->method('update');
|
|
|
|
$response = $this->controller->update($threadId, null, true);
|
|
|
|
$this->assertEquals(Http::STATUS_FORBIDDEN, $response->getStatus());
|
|
$this->assertEquals(['error' => 'You do not have permission to modify thread status'], $response->getData());
|
|
}
|
|
|
|
public function testDestroyThreadSuccessfully(): void {
|
|
$threadId = 1;
|
|
$categoryId = 1;
|
|
$thread = $this->createMockThread($threadId, $categoryId, 'user1', 'Test Thread');
|
|
|
|
// Mock category
|
|
$category = new Category();
|
|
$category->setId($categoryId);
|
|
$category->setSlug('test-category');
|
|
$category->setThreadCount(5);
|
|
$category->setPostCount(20);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->categoryMapper->expects($this->once())
|
|
->method('find')
|
|
->with($categoryId)
|
|
->willReturn($category);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedThread) {
|
|
$this->assertNotNull($updatedThread->getDeletedAt());
|
|
return $updatedThread;
|
|
});
|
|
|
|
$this->categoryMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturn($category);
|
|
|
|
$response = $this->controller->destroy($threadId);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$this->assertEquals(['success' => true, 'categorySlug' => 'test-category'], $response->getData());
|
|
}
|
|
|
|
public function testDestroyThreadReturnsNotFoundWhenThreadDoesNotExist(): void {
|
|
$threadId = 999;
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willThrowException(new DoesNotExistException('Thread not found'));
|
|
|
|
$response = $this->controller->destroy($threadId);
|
|
|
|
$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
|
|
$this->assertEquals(['error' => 'Thread not found'], $response->getData());
|
|
}
|
|
|
|
public function testByAuthorReturnsThreadsSuccessfully(): void {
|
|
$authorId = 'user1';
|
|
$limit = 50;
|
|
$offset = 0;
|
|
|
|
$thread1 = $this->createMockThread(1, 1, $authorId, 'Thread 1');
|
|
$thread2 = $this->createMockThread(2, 1, $authorId, 'Thread 2');
|
|
$threads = [$thread1, $thread2];
|
|
|
|
$enrichedAuthor = [
|
|
'userId' => $authorId,
|
|
'displayName' => 'Test User',
|
|
];
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('findByAuthorId')
|
|
->with($authorId, $limit, $offset)
|
|
->willReturn($threads);
|
|
|
|
$this->userService->expects($this->once())
|
|
->method('enrichUserData')
|
|
->with($authorId)
|
|
->willReturn($enrichedAuthor);
|
|
|
|
$this->threadEnrichmentService->expects($this->exactly(2))
|
|
->method('enrichThread')
|
|
->willReturnCallback(function ($thread, $author) {
|
|
$data = $thread->jsonSerialize();
|
|
$data['author'] = $author;
|
|
return $data;
|
|
});
|
|
|
|
$response = $this->controller->byAuthor($authorId, $limit, $offset);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertIsArray($data);
|
|
$this->assertCount(2, $data);
|
|
$this->assertEquals(1, $data[0]['id']);
|
|
$this->assertEquals(2, $data[1]['id']);
|
|
$this->assertEquals($enrichedAuthor, $data[0]['author']);
|
|
}
|
|
|
|
public function testSetLockedUpdatesThreadSuccessfully(): void {
|
|
$threadId = 1;
|
|
$thread = $this->createMockThread($threadId, 1, 'user1', 'Test Thread');
|
|
$thread->setIsLocked(false);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedThread) {
|
|
$this->assertTrue($updatedThread->getIsLocked());
|
|
return $updatedThread;
|
|
});
|
|
|
|
$this->threadEnrichmentService->expects($this->once())
|
|
->method('enrichThread')
|
|
->willReturnCallback(function ($thread) {
|
|
return $thread->jsonSerialize();
|
|
});
|
|
|
|
$response = $this->controller->setLocked($threadId, true);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertEquals($threadId, $data['id']);
|
|
}
|
|
|
|
public function testSetLockedReturnsNotFoundWhenThreadDoesNotExist(): void {
|
|
$threadId = 999;
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willThrowException(new DoesNotExistException('Thread not found'));
|
|
|
|
$response = $this->controller->setLocked($threadId, true);
|
|
|
|
$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
|
|
$this->assertEquals(['error' => 'Thread not found'], $response->getData());
|
|
}
|
|
|
|
public function testSetPinnedUpdatesThreadSuccessfully(): void {
|
|
$threadId = 1;
|
|
$thread = $this->createMockThread($threadId, 1, 'user1', 'Test Thread');
|
|
$thread->setIsPinned(false);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedThread) {
|
|
$this->assertTrue($updatedThread->getIsPinned());
|
|
return $updatedThread;
|
|
});
|
|
|
|
$this->threadEnrichmentService->expects($this->once())
|
|
->method('enrichThread')
|
|
->willReturnCallback(function ($thread) {
|
|
return $thread->jsonSerialize();
|
|
});
|
|
|
|
$response = $this->controller->setPinned($threadId, true);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertEquals($threadId, $data['id']);
|
|
}
|
|
|
|
public function testSetPinnedReturnsNotFoundWhenThreadDoesNotExist(): void {
|
|
$threadId = 999;
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willThrowException(new DoesNotExistException('Thread not found'));
|
|
|
|
$response = $this->controller->setPinned($threadId, true);
|
|
|
|
$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
|
|
$this->assertEquals(['error' => 'Thread not found'], $response->getData());
|
|
}
|
|
|
|
public function testMoveThreadSuccessfully(): void {
|
|
$threadId = 1;
|
|
$oldCategoryId = 1;
|
|
$newCategoryId = 2;
|
|
$moderatorId = 'moderator1';
|
|
|
|
$thread = $this->createMockThread($threadId, $oldCategoryId, 'user1', 'Test Thread');
|
|
$thread->setPostCount(5);
|
|
|
|
$oldCategory = new Category();
|
|
$oldCategory->setId($oldCategoryId);
|
|
$oldCategory->setThreadCount(10);
|
|
$oldCategory->setPostCount(50);
|
|
|
|
$newCategory = new Category();
|
|
$newCategory->setId($newCategoryId);
|
|
$newCategory->setThreadCount(3);
|
|
$newCategory->setPostCount(15);
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($moderatorId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->categoryMapper->expects($this->exactly(2))
|
|
->method('find')
|
|
->willReturnCallback(function ($id) use ($oldCategory, $newCategory, $oldCategoryId, $newCategoryId) {
|
|
if ($id === $newCategoryId) {
|
|
return $newCategory;
|
|
} elseif ($id === $oldCategoryId) {
|
|
return $oldCategory;
|
|
}
|
|
throw new DoesNotExistException('Category not found');
|
|
});
|
|
|
|
// User has moderation permission on target category
|
|
$this->permissionService->expects($this->once())
|
|
->method('hasCategoryPermission')
|
|
->with($moderatorId, $newCategoryId, 'canModerate')
|
|
->willReturn(true);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($updatedThread) use ($newCategoryId) {
|
|
$this->assertEquals($newCategoryId, $updatedThread->getCategoryId());
|
|
return $updatedThread;
|
|
});
|
|
|
|
// Verify category counts are updated
|
|
$this->categoryMapper->expects($this->exactly(2))
|
|
->method('update')
|
|
->willReturnCallback(function ($category) use ($oldCategoryId, $newCategoryId) {
|
|
if ($category->getId() === $oldCategoryId) {
|
|
// Old category should have decremented counts
|
|
$this->assertEquals(9, $category->getThreadCount());
|
|
$this->assertEquals(45, $category->getPostCount());
|
|
} elseif ($category->getId() === $newCategoryId) {
|
|
// New category should have incremented counts
|
|
$this->assertEquals(4, $category->getThreadCount());
|
|
$this->assertEquals(20, $category->getPostCount());
|
|
}
|
|
return $category;
|
|
});
|
|
|
|
$response = $this->controller->move($threadId, $newCategoryId);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertEquals($threadId, $data['id']);
|
|
}
|
|
|
|
public function testMoveThreadReturnsUnauthorizedWhenUserNotAuthenticated(): void {
|
|
$threadId = 1;
|
|
$newCategoryId = 2;
|
|
|
|
$this->userSession->method('getUser')->willReturn(null);
|
|
|
|
// Thread should not be queried if user is not authenticated
|
|
$this->threadMapper->expects($this->never())
|
|
->method('find');
|
|
|
|
$this->threadMapper->expects($this->never())
|
|
->method('update');
|
|
|
|
$response = $this->controller->move($threadId, $newCategoryId);
|
|
|
|
$this->assertEquals(Http::STATUS_UNAUTHORIZED, $response->getStatus());
|
|
$this->assertEquals(['error' => 'User not authenticated'], $response->getData());
|
|
}
|
|
|
|
public function testMoveThreadReturnsNotFoundWhenThreadDoesNotExist(): void {
|
|
$threadId = 999;
|
|
$newCategoryId = 2;
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn('moderator1');
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willThrowException(new DoesNotExistException('Thread not found'));
|
|
|
|
$response = $this->controller->move($threadId, $newCategoryId);
|
|
|
|
$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
|
|
$this->assertEquals(['error' => 'Thread not found'], $response->getData());
|
|
}
|
|
|
|
public function testMoveThreadReturnsNotFoundWhenTargetCategoryDoesNotExist(): void {
|
|
$threadId = 1;
|
|
$oldCategoryId = 1;
|
|
$newCategoryId = 999;
|
|
|
|
$thread = $this->createMockThread($threadId, $oldCategoryId, 'user1', 'Test Thread');
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn('moderator1');
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->categoryMapper->expects($this->once())
|
|
->method('find')
|
|
->with($newCategoryId)
|
|
->willThrowException(new DoesNotExistException('Category not found'));
|
|
|
|
$this->threadMapper->expects($this->never())
|
|
->method('update');
|
|
|
|
$response = $this->controller->move($threadId, $newCategoryId);
|
|
|
|
$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
|
|
$this->assertEquals(['error' => 'Target category not found'], $response->getData());
|
|
}
|
|
|
|
public function testMoveThreadReturnsForbiddenWhenUserLacksPermissionOnTargetCategory(): void {
|
|
$threadId = 1;
|
|
$oldCategoryId = 1;
|
|
$newCategoryId = 2;
|
|
$userId = 'user1';
|
|
|
|
$thread = $this->createMockThread($threadId, $oldCategoryId, $userId, 'Test Thread');
|
|
|
|
$newCategory = new Category();
|
|
$newCategory->setId($newCategoryId);
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($userId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->with($threadId)
|
|
->willReturn($thread);
|
|
|
|
$this->categoryMapper->expects($this->once())
|
|
->method('find')
|
|
->with($newCategoryId)
|
|
->willReturn($newCategory);
|
|
|
|
// User does not have moderation permission on target category
|
|
$this->permissionService->expects($this->once())
|
|
->method('hasCategoryPermission')
|
|
->with($userId, $newCategoryId, 'canModerate')
|
|
->willReturn(false);
|
|
|
|
$this->threadMapper->expects($this->never())
|
|
->method('update');
|
|
|
|
$response = $this->controller->move($threadId, $newCategoryId);
|
|
|
|
$this->assertEquals(Http::STATUS_FORBIDDEN, $response->getStatus());
|
|
$this->assertEquals(
|
|
['error' => 'You do not have permission to move threads to this category'],
|
|
$response->getData()
|
|
);
|
|
}
|
|
|
|
public function testMoveThreadUpdatesCategoryCounts(): void {
|
|
$threadId = 1;
|
|
$oldCategoryId = 1;
|
|
$newCategoryId = 2;
|
|
$moderatorId = 'moderator1';
|
|
$threadPostCount = 7;
|
|
|
|
$thread = $this->createMockThread($threadId, $oldCategoryId, 'user1', 'Test Thread');
|
|
$thread->setPostCount($threadPostCount);
|
|
|
|
$oldCategory = new Category();
|
|
$oldCategory->setId($oldCategoryId);
|
|
$oldCategory->setThreadCount(15);
|
|
$oldCategory->setPostCount(100);
|
|
|
|
$newCategory = new Category();
|
|
$newCategory->setId($newCategoryId);
|
|
$newCategory->setThreadCount(5);
|
|
$newCategory->setPostCount(30);
|
|
|
|
$user = $this->createMock(IUser::class);
|
|
$user->method('getUID')->willReturn($moderatorId);
|
|
$this->userSession->method('getUser')->willReturn($user);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('find')
|
|
->willReturn($thread);
|
|
|
|
$this->categoryMapper->expects($this->exactly(2))
|
|
->method('find')
|
|
->willReturnCallback(function ($id) use ($oldCategory, $newCategory, $oldCategoryId, $newCategoryId) {
|
|
if ($id === $newCategoryId) {
|
|
return $newCategory;
|
|
} elseif ($id === $oldCategoryId) {
|
|
return $oldCategory;
|
|
}
|
|
throw new DoesNotExistException('Category not found');
|
|
});
|
|
|
|
$this->permissionService->expects($this->once())
|
|
->method('hasCategoryPermission')
|
|
->willReturn(true);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('update')
|
|
->willReturn($thread);
|
|
|
|
$oldCategoryUpdated = false;
|
|
$newCategoryUpdated = false;
|
|
|
|
$this->categoryMapper->expects($this->exactly(2))
|
|
->method('update')
|
|
->willReturnCallback(function ($category) use (
|
|
$oldCategoryId,
|
|
$newCategoryId,
|
|
$threadPostCount,
|
|
&$oldCategoryUpdated,
|
|
&$newCategoryUpdated
|
|
) {
|
|
if ($category->getId() === $oldCategoryId) {
|
|
// Old category: thread count -1, post count -7
|
|
$this->assertEquals(14, $category->getThreadCount());
|
|
$this->assertEquals(93, $category->getPostCount());
|
|
$oldCategoryUpdated = true;
|
|
} elseif ($category->getId() === $newCategoryId) {
|
|
// New category: thread count +1, post count +7
|
|
$this->assertEquals(6, $category->getThreadCount());
|
|
$this->assertEquals(37, $category->getPostCount());
|
|
$newCategoryUpdated = true;
|
|
}
|
|
return $category;
|
|
});
|
|
|
|
$response = $this->controller->move($threadId, $newCategoryId);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$this->assertTrue($oldCategoryUpdated, 'Old category should have been updated');
|
|
$this->assertTrue($newCategoryUpdated, 'New category should have been updated');
|
|
}
|
|
|
|
public function testByCategoryPaginatedReturnsThreadsWithPaginationMetadata(): void {
|
|
$categoryId = 1;
|
|
$page = 1;
|
|
$perPage = 20;
|
|
|
|
$thread1 = $this->createMockThread(1, $categoryId, 'user1', 'Test Thread 1');
|
|
$thread2 = $this->createMockThread(2, $categoryId, 'user2', 'Test Thread 2');
|
|
$threads = [$thread1, $thread2];
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('countByCategoryId')
|
|
->with($categoryId)
|
|
->willReturn(2);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('findByCategoryId')
|
|
->with($categoryId, $perPage, 0)
|
|
->willReturn($threads);
|
|
|
|
$this->userService->expects($this->once())
|
|
->method('enrichMultipleUsers')
|
|
->willReturn([
|
|
'user1' => ['userId' => 'user1', 'displayName' => 'User 1'],
|
|
'user2' => ['userId' => 'user2', 'displayName' => 'User 2'],
|
|
]);
|
|
|
|
$response = $this->controller->byCategoryPaginated($categoryId, $page, $perPage);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertArrayHasKey('threads', $data);
|
|
$this->assertArrayHasKey('pagination', $data);
|
|
$this->assertCount(2, $data['threads']);
|
|
$this->assertEquals(1, $data['pagination']['page']);
|
|
$this->assertEquals(20, $data['pagination']['perPage']);
|
|
$this->assertEquals(2, $data['pagination']['total']);
|
|
$this->assertEquals(1, $data['pagination']['totalPages']);
|
|
}
|
|
|
|
public function testByCategoryPaginatedCalculatesTotalPagesCorrectly(): void {
|
|
$categoryId = 1;
|
|
$page = 1;
|
|
$perPage = 20;
|
|
|
|
// 45 threads = 3 pages (20 + 20 + 5)
|
|
$this->threadMapper->expects($this->once())
|
|
->method('countByCategoryId')
|
|
->with($categoryId)
|
|
->willReturn(45);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('findByCategoryId')
|
|
->with($categoryId, $perPage, 0)
|
|
->willReturn([]);
|
|
|
|
$this->userService->method('enrichMultipleUsers')->willReturn([]);
|
|
|
|
$response = $this->controller->byCategoryPaginated($categoryId, $page, $perPage);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertEquals(3, $data['pagination']['totalPages']);
|
|
$this->assertEquals(45, $data['pagination']['total']);
|
|
}
|
|
|
|
public function testByCategoryPaginatedFetchesCorrectOffset(): void {
|
|
$categoryId = 1;
|
|
$page = 3;
|
|
$perPage = 20;
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('countByCategoryId')
|
|
->with($categoryId)
|
|
->willReturn(60);
|
|
|
|
// Page 3 should have offset 40 (2 * 20)
|
|
$this->threadMapper->expects($this->once())
|
|
->method('findByCategoryId')
|
|
->with($categoryId, $perPage, 40)
|
|
->willReturn([]);
|
|
|
|
$this->userService->method('enrichMultipleUsers')->willReturn([]);
|
|
|
|
$response = $this->controller->byCategoryPaginated($categoryId, $page, $perPage);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertEquals(3, $data['pagination']['page']);
|
|
}
|
|
|
|
public function testByCategoryPaginatedClampsPageToValidRange(): void {
|
|
$categoryId = 1;
|
|
$perPage = 20;
|
|
|
|
// Only 30 threads = 2 pages
|
|
$this->threadMapper->expects($this->once())
|
|
->method('countByCategoryId')
|
|
->with($categoryId)
|
|
->willReturn(30);
|
|
|
|
// Request page 5 but should be clamped to page 2 (offset 20)
|
|
$this->threadMapper->expects($this->once())
|
|
->method('findByCategoryId')
|
|
->with($categoryId, $perPage, 20)
|
|
->willReturn([]);
|
|
|
|
$this->userService->method('enrichMultipleUsers')->willReturn([]);
|
|
|
|
$response = $this->controller->byCategoryPaginated($categoryId, 5, $perPage);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertEquals(2, $data['pagination']['page']); // Clamped to max page
|
|
$this->assertEquals(2, $data['pagination']['totalPages']);
|
|
}
|
|
|
|
public function testByCategoryPaginatedHandlesEmptyCategory(): void {
|
|
$categoryId = 1;
|
|
$page = 1;
|
|
$perPage = 20;
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('countByCategoryId')
|
|
->with($categoryId)
|
|
->willReturn(0);
|
|
|
|
$this->threadMapper->expects($this->once())
|
|
->method('findByCategoryId')
|
|
->with($categoryId, $perPage, 0)
|
|
->willReturn([]);
|
|
|
|
$this->userService->method('enrichMultipleUsers')->willReturn([]);
|
|
|
|
$response = $this->controller->byCategoryPaginated($categoryId, $page, $perPage);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
|
$data = $response->getData();
|
|
$this->assertCount(0, $data['threads']);
|
|
$this->assertEquals(1, $data['pagination']['page']);
|
|
$this->assertEquals(1, $data['pagination']['totalPages']);
|
|
$this->assertEquals(0, $data['pagination']['total']);
|
|
}
|
|
|
|
private function createMockThread(int $id, int $categoryId, string $authorId, string $title): Thread {
|
|
$thread = new Thread();
|
|
$thread->setId($id);
|
|
$thread->setCategoryId($categoryId);
|
|
$thread->setAuthorId($authorId);
|
|
$thread->setTitle($title);
|
|
$thread->setSlug("thread-$id");
|
|
$thread->setViewCount(0);
|
|
$thread->setPostCount(0);
|
|
$thread->setIsLocked(false);
|
|
$thread->setIsPinned(false);
|
|
$thread->setIsHidden(false);
|
|
$thread->setCreatedAt(time());
|
|
$thread->setUpdatedAt(time());
|
|
return $thread;
|
|
}
|
|
}
|