fix: remove/consolidate redundant endpoints

This commit is contained in:
2026-04-03 00:49:47 +03:00
parent 19f6304190
commit ba075a7c9b
9 changed files with 86 additions and 626 deletions

View File

@@ -63,65 +63,7 @@ class PostController extends OCSController {
}
/**
* Get posts by thread
*
* @param int $threadId Thread ID
* @param int<1, 200> $limit Maximum number of posts to return
* @param int $offset Offset for pagination
* @return DataResponse<Http::STATUS_OK, list<array<string, mixed>>, array{}>
*
* 200: Posts returned
*/
#[NoAdminRequired]
#[PublicPage]
#[RequirePermission('canView', resourceType: 'category', resourceIdFromThreadId: 'threadId')]
#[ApiRoute(verb: 'GET', url: '/api/threads/{threadId}/posts')]
public function byThread(int $threadId, int $limit = 50, int $offset = 0): DataResponse {
try {
$posts = $this->postMapper->findByThreadId($threadId, $limit, $offset);
// Prefetch BBCodes once for all posts to avoid repeated queries
$bbcodes = $this->bbCodeMapper->findAllEnabled();
// Fetch reactions for all posts at once (performance optimization)
$postIds = array_map(fn ($p) => $p->getId(), $posts);
$reactions = $this->reactionMapper->findByPostIds($postIds);
// Group reactions by post ID
$reactionsByPostId = [];
foreach ($reactions as $reaction) {
$postId = $reaction->getPostId();
if (!isset($reactionsByPostId[$postId])) {
$reactionsByPostId[$postId] = [];
}
$reactionsByPostId[$postId][] = $reaction;
}
// Get current user ID to mark user's reactions
$currentUserId = $this->userSession->getUser()?->getUID();
// Extract unique author IDs
$authorIds = array_unique(array_map(fn ($p) => $p->getAuthorId(), $posts));
// Batch fetch author data (includes roles)
$authors = $this->userService->enrichMultipleUsers($authorIds);
// Get category ID for permission checks
$categoryId = $this->permissionService->getCategoryIdFromThread($threadId);
// Enrich posts with content, reactions, and pre-fetched author data
return new DataResponse(array_map(function ($p) use ($bbcodes, $reactionsByPostId, $currentUserId, $authors, $categoryId) {
$postReactions = $reactionsByPostId[$p->getId()] ?? [];
return $this->postEnrichmentService->enrichPost($p, $bbcodes, $postReactions, $currentUserId, $authors[$p->getAuthorId()], $categoryId);
}, $posts));
} catch (\Exception $e) {
$this->logger->error('Error fetching posts by thread: ' . $e->getMessage());
return new DataResponse(['error' => 'Failed to fetch posts'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
/**
* Get paginated posts by thread with first post separated
* Get posts by thread with first post separated
*
* @param int $threadId Thread ID
* @param int $page Page number (1-indexed)
@@ -133,8 +75,8 @@ class PostController extends OCSController {
#[NoAdminRequired]
#[PublicPage]
#[RequirePermission('canView', resourceType: 'category', resourceIdFromThreadId: 'threadId')]
#[ApiRoute(verb: 'GET', url: '/api/threads/{threadId}/posts/paginated')]
public function byThreadPaginated(int $threadId, int $page = 0, int $perPage = 20): DataResponse {
#[ApiRoute(verb: 'GET', url: '/api/threads/{threadId}/posts')]
public function byThread(int $threadId, int $page = 0, int $perPage = 20): DataResponse {
try {
// Get current user ID
$currentUserId = $this->userSession->getUser()?->getUID();
@@ -245,7 +187,7 @@ class PostController extends OCSController {
],
]);
} catch (\Exception $e) {
$this->logger->error('Error fetching paginated posts by thread: ' . $e->getMessage());
$this->logger->error('Error fetching posts by thread: ' . $e->getMessage());
return new DataResponse(['error' => 'Failed to fetch posts'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}

View File

@@ -105,51 +105,6 @@ class ThreadController extends OCSController {
* Get threads by category
*
* @param int $categoryId Category ID
* @param int<1, 200> $limit Maximum number of threads to return
* @param int $offset Offset for pagination
* @return DataResponse<Http::STATUS_OK, list<array<string, mixed>>, array{}>
*
* 200: Threads returned
*/
#[NoAdminRequired]
#[PublicPage]
#[RequirePermission('canView', resourceType: 'category', resourceIdParam: 'categoryId')]
#[ApiRoute(verb: 'GET', url: '/api/categories/{categoryId}/threads')]
public function byCategory(int $categoryId, int $limit = 50, int $offset = 0): DataResponse {
try {
$threads = $this->threadMapper->findByCategoryId($categoryId, $limit, $offset);
// Extract unique author IDs (thread authors + last reply authors)
$authorIds = array_map(fn ($t) => $t->getAuthorId(), $threads);
$lastReplyAuthorIds = array_filter(array_map(fn ($t) => $t->getLastReplyAuthorId(), $threads));
$allAuthorIds = array_unique(array_merge($authorIds, $lastReplyAuthorIds));
// Batch fetch author data (includes roles)
$authors = $this->userService->enrichMultipleUsers($allAuthorIds);
// Enrich threads with pre-fetched author data and last reply info
return new DataResponse(array_map(function ($t) use ($authors) {
$lastReply = null;
$lastReplyAuthorId = $t->getLastReplyAuthorId();
if ($lastReplyAuthorId !== null) {
$lastReply = [
'postId' => $t->getLastPostId(),
'author' => $authors[$lastReplyAuthorId] ?? null,
'createdAt' => $t->getLastReplyAt(),
];
}
return $this->threadEnrichmentService->enrichThread($t, $authors[$t->getAuthorId()] ?? null, $lastReply);
}, $threads));
} catch (\Exception $e) {
$this->logger->error('Error fetching threads by category: ' . $e->getMessage());
return new DataResponse(['error' => 'Failed to fetch threads'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
/**
* Get paginated threads by category
*
* @param int $categoryId Category ID
* @param int $page Page number (1-indexed)
* @param int $perPage Number of threads per page
* @return DataResponse<Http::STATUS_OK, array{threads: list<array<string, mixed>>, pagination: array{page: int, perPage: int, total: int, totalPages: int}}, array{}>
@@ -159,8 +114,8 @@ class ThreadController extends OCSController {
#[NoAdminRequired]
#[PublicPage]
#[RequirePermission('canView', resourceType: 'category', resourceIdParam: 'categoryId')]
#[ApiRoute(verb: 'GET', url: '/api/categories/{categoryId}/threads/paginated')]
public function byCategoryPaginated(int $categoryId, int $page = 1, int $perPage = 20): DataResponse {
#[ApiRoute(verb: 'GET', url: '/api/categories/{categoryId}/threads')]
public function byCategory(int $categoryId, int $page = 1, int $perPage = 20): DataResponse {
try {
// Count total threads in category
$totalThreads = $this->threadMapper->countByCategoryId($categoryId);
@@ -205,7 +160,7 @@ class ThreadController extends OCSController {
],
]);
} catch (\Exception $e) {
$this->logger->error('Error fetching paginated threads by category: ' . $e->getMessage());
$this->logger->error('Error fetching threads by category: ' . $e->getMessage());
return new DataResponse(['error' => 'Failed to fetch threads'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}

View File

@@ -6089,107 +6089,7 @@
"/ocs/v2.php/apps/forum/api/threads/{threadId}/posts": {
"get": {
"operationId": "post-by-thread",
"summary": "Get posts by thread",
"tags": [
"post"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "threadId",
"in": "path",
"description": "Thread ID",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
},
{
"name": "limit",
"in": "query",
"description": "Maximum number of posts to return",
"schema": {
"type": "integer",
"format": "int64",
"default": 50,
"minimum": 1,
"maximum": 200
}
},
{
"name": "offset",
"in": "query",
"description": "Offset for pagination",
"schema": {
"type": "integer",
"format": "int64",
"default": 0
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Posts returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": {
"type": "object"
}
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/forum/api/threads/{threadId}/posts/paginated": {
"get": {
"operationId": "post-by-thread-paginated",
"summary": "Get paginated posts by thread with first post separated",
"summary": "Get posts by thread with first post separated",
"tags": [
"post"
],
@@ -10513,106 +10413,6 @@
"basic_auth": []
}
],
"parameters": [
{
"name": "categoryId",
"in": "path",
"description": "Category ID",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
},
{
"name": "limit",
"in": "query",
"description": "Maximum number of threads to return",
"schema": {
"type": "integer",
"format": "int64",
"default": 50,
"minimum": 1,
"maximum": 200
}
},
{
"name": "offset",
"in": "query",
"description": "Offset for pagination",
"schema": {
"type": "integer",
"format": "int64",
"default": 0
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Threads returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": {
"type": "object"
}
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/forum/api/categories/{categoryId}/threads/paginated": {
"get": {
"operationId": "thread-by-category-paginated",
"summary": "Get paginated threads by category",
"tags": [
"thread"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "categoryId",

View File

@@ -6089,107 +6089,7 @@
"/ocs/v2.php/apps/forum/api/threads/{threadId}/posts": {
"get": {
"operationId": "post-by-thread",
"summary": "Get posts by thread",
"tags": [
"post"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "threadId",
"in": "path",
"description": "Thread ID",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
},
{
"name": "limit",
"in": "query",
"description": "Maximum number of posts to return",
"schema": {
"type": "integer",
"format": "int64",
"default": 50,
"minimum": 1,
"maximum": 200
}
},
{
"name": "offset",
"in": "query",
"description": "Offset for pagination",
"schema": {
"type": "integer",
"format": "int64",
"default": 0
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Posts returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": {
"type": "object"
}
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/forum/api/threads/{threadId}/posts/paginated": {
"get": {
"operationId": "post-by-thread-paginated",
"summary": "Get paginated posts by thread with first post separated",
"summary": "Get posts by thread with first post separated",
"tags": [
"post"
],
@@ -10513,106 +10413,6 @@
"basic_auth": []
}
],
"parameters": [
{
"name": "categoryId",
"in": "path",
"description": "Category ID",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
},
{
"name": "limit",
"in": "query",
"description": "Maximum number of threads to return",
"schema": {
"type": "integer",
"format": "int64",
"default": 50,
"minimum": 1,
"maximum": 200
}
},
{
"name": "offset",
"in": "query",
"description": "Offset for pagination",
"schema": {
"type": "integer",
"format": "int64",
"default": 0
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Threads returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": {
"type": "object"
}
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/forum/api/categories/{categoryId}/threads/paginated": {
"get": {
"operationId": "thread-by-category-paginated",
"summary": "Get paginated threads by category",
"tags": [
"thread"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "categoryId",

View File

@@ -307,15 +307,12 @@ export default defineComponent({
}
}
const resp = await ocs.get<PaginatedResponse>(
`/categories/${this.category!.id}/threads/paginated`,
{
params: {
page,
perPage: this.perPage,
},
const resp = await ocs.get<PaginatedResponse>(`/categories/${this.category!.id}/threads`, {
params: {
page,
perPage: this.perPage,
},
)
})
const data = resp.data
if (data) {

View File

@@ -616,15 +616,12 @@ export default defineComponent({
}
}
const resp = await ocs.get<PaginatedResponse>(
`/threads/${this.thread!.id}/posts/paginated`,
{
params: {
page,
perPage: this.perPage,
},
const resp = await ocs.get<PaginatedResponse>(`/threads/${this.thread!.id}/posts`, {
params: {
page,
perPage: this.perPage,
},
)
})
const data = resp.data
if (data) {

View File

@@ -158,7 +158,7 @@ describe('ThreadView', () => {
mockFetchThread.mockResolvedValue(mockThread.value)
mockCheckCategoryPermission.mockResolvedValue(false)
mockOcsGet.mockImplementation((url: string) => {
if (url.includes('/posts/paginated')) {
if (url.includes('/posts')) {
return mockGetResponse({
data: {
firstPost: mockFirstPost,
@@ -360,7 +360,7 @@ describe('ThreadView', () => {
})
mockOcsGet.mockImplementation((url: string) => {
if (url.includes('/posts/paginated')) {
if (url.includes('/posts')) {
return mockGetResponse({
data: {
firstPost: guestFirstPost,
@@ -407,7 +407,7 @@ describe('ThreadView', () => {
})
mockOcsGet.mockImplementation((url: string) => {
if (url.includes('/posts/paginated')) {
if (url.includes('/posts')) {
return mockGetResponse({
data: {
firstPost: mockFirstPost,
@@ -459,7 +459,7 @@ describe('ThreadView', () => {
mockFetchThread.mockResolvedValue(mockThread.value)
mockOcsGet.mockImplementation((url: string) => {
if (url.includes('/posts/paginated')) {
if (url.includes('/posts')) {
return mockGetResponse({
data: {
firstPost: mockFirstPost,
@@ -505,7 +505,7 @@ describe('ThreadView', () => {
mockFetchThread.mockResolvedValue(mockThread.value)
mockOcsGet.mockImplementation((url: string) => {
if (url.includes('/posts/paginated')) {
if (url.includes('/posts')) {
return mockGetResponse({
data: {
firstPost: mockFirstPost,
@@ -542,7 +542,7 @@ describe('ThreadView', () => {
const guestReply = createMockPost({ id: 10, authorId: guestAuthorId, author: guestAuthor })
mockOcsGet.mockImplementation((url: string) => {
if (url.includes('/posts/paginated')) {
if (url.includes('/posts')) {
return mockGetResponse({
data: {
firstPost: mockFirstPost,

View File

@@ -133,75 +133,25 @@ class PostControllerTest extends TestCase {
);
}
public function testByThreadReturnsPostsSuccessfully(): void {
$threadId = 1;
$limit = 50;
$offset = 0;
// Create mock posts
$post1 = $this->createMockPost(1, $threadId, 'user1', 'Test content 1');
$post2 = $this->createMockPost(2, $threadId, 'user2', 'Test content 2');
$posts = [$post1, $post2];
// Create mock BBCode
$bbcode = new BBCode();
$bbcode->setId(1);
$bbcode->setTag('b');
$bbcode->setReplacement('<strong>{content}</strong>');
$bbcode->setEnabled(true);
// Create mock reactions
$reaction1 = $this->createMockReaction(1, 1, 'user1', '👍');
$reaction2 = $this->createMockReaction(2, 1, 'user2', '👍');
// Mock user session
$user = $this->createMock(IUser::class);
$user->method('getUID')->willReturn('user1');
$this->userSession->method('getUser')->willReturn($user);
// Set up expectations
$this->postMapper->expects($this->once())
->method('findByThreadId')
->with($threadId, $limit, $offset)
->willReturn($posts);
$this->bbCodeMapper->expects($this->once())
->method('findAllEnabled')
->willReturn([$bbcode]);
$this->reactionMapper->expects($this->once())
->method('findByPostIds')
->with([1, 2])
->willReturn([$reaction1, $reaction2]);
// Mock userService to return enriched user data
$this->userService->expects($this->once())
->method('enrichMultipleUsers')
->with(['user1', 'user2'])
->willReturn([
'user1' => ['userId' => 'user1', 'displayName' => 'User 1', 'roles' => []],
'user2' => ['userId' => 'user2', 'displayName' => 'User 2', 'roles' => []],
]);
// Execute
$response = $this->controller->byThread($threadId, $limit, $offset);
// Assert
$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->assertArrayHasKey('reactions', $data[0]);
$this->assertArrayHasKey('reactions', $data[1]);
}
public function testByThreadHandlesEmptyPosts(): void {
$threadId = 1;
// Mock user session (no user = guest)
$this->userSession->method('getUser')->willReturn(null);
$this->postMapper->expects($this->once())
->method('findByThreadId')
->method('countRepliesByThreadId')
->with($threadId)
->willReturn(0);
$this->postMapper->expects($this->once())
->method('findFirstPostByThreadId')
->with($threadId)
->willReturn(null);
$this->postMapper->expects($this->once())
->method('findRepliesByThreadId')
->with($threadId, 20, 0)
->willReturn([]);
$this->bbCodeMapper->expects($this->once())
@@ -213,11 +163,20 @@ class PostControllerTest extends TestCase {
->with([])
->willReturn([]);
$this->userService->method('enrichMultipleUsers')->willReturn([]);
$response = $this->controller->byThread($threadId);
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
$this->assertIsArray($response->getData());
$this->assertCount(0, $response->getData());
$data = $response->getData();
$this->assertArrayHasKey('firstPost', $data);
$this->assertArrayHasKey('replies', $data);
$this->assertArrayHasKey('pagination', $data);
$this->assertNull($data['firstPost']);
$this->assertCount(0, $data['replies']);
$this->assertEquals(1, $data['pagination']['page']);
$this->assertEquals(1, $data['pagination']['totalPages']);
$this->assertEquals(0, $data['pagination']['total']);
}
public function testShowReturnsPostSuccessfully(): void {
@@ -978,7 +937,7 @@ class PostControllerTest extends TestCase {
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
}
public function testByThreadPaginatedReturnsPostsSuccessfully(): void {
public function testByThreadReturnsPostsSuccessfully(): void {
$threadId = 1;
$page = 1;
$perPage = 20;
@@ -1043,7 +1002,7 @@ class PostControllerTest extends TestCase {
]);
// Execute
$response = $this->controller->byThreadPaginated($threadId, $page, $perPage);
$response = $this->controller->byThread($threadId, $page, $perPage);
// Assert
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
@@ -1059,7 +1018,7 @@ class PostControllerTest extends TestCase {
$this->assertEquals(1, $data['pagination']['totalPages']);
}
public function testByThreadPaginatedStartsAtLastPageForUnreadThread(): void {
public function testByThreadStartsAtLastPageForUnreadThread(): void {
$threadId = 1;
$perPage = 20;
@@ -1109,7 +1068,7 @@ class PostControllerTest extends TestCase {
]);
// Execute with page=0 (auto-calculate start page)
$response = $this->controller->byThreadPaginated($threadId, 0, $perPage);
$response = $this->controller->byThread($threadId, 0, $perPage);
// Assert
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
@@ -1120,7 +1079,7 @@ class PostControllerTest extends TestCase {
$this->assertNull($data['pagination']['lastReadPostId']);
}
public function testByThreadPaginatedStartsAtOldestUnreadPage(): void {
public function testByThreadStartsAtOldestUnreadPage(): void {
$threadId = 1;
$perPage = 20;
@@ -1185,7 +1144,7 @@ class PostControllerTest extends TestCase {
]);
// Execute with page=0 (auto-calculate start page)
$response = $this->controller->byThreadPaginated($threadId, 0, $perPage);
$response = $this->controller->byThread($threadId, 0, $perPage);
// Assert
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
@@ -1196,7 +1155,7 @@ class PostControllerTest extends TestCase {
$this->assertEquals(31, $data['pagination']['lastReadPostId']);
}
public function testByThreadPaginatedGoesToLastPageWhenAllRead(): void {
public function testByThreadGoesToLastPageWhenAllRead(): void {
$threadId = 1;
$perPage = 20;
@@ -1252,7 +1211,7 @@ class PostControllerTest extends TestCase {
]);
// Execute with page=0 (auto-calculate start page)
$response = $this->controller->byThreadPaginated($threadId, 0, $perPage);
$response = $this->controller->byThread($threadId, 0, $perPage);
// Assert
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
@@ -1263,7 +1222,7 @@ class PostControllerTest extends TestCase {
$this->assertEquals(50, $data['pagination']['lastReadPostId']);
}
public function testByThreadPaginatedWorksForGuestUser(): void {
public function testByThreadWorksForGuestUser(): void {
$threadId = 1;
$page = 1;
$perPage = 20;
@@ -1302,7 +1261,7 @@ class PostControllerTest extends TestCase {
]);
// Execute
$response = $this->controller->byThreadPaginated($threadId, $page, $perPage);
$response = $this->controller->byThread($threadId, $page, $perPage);
// Assert
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
@@ -1312,7 +1271,7 @@ class PostControllerTest extends TestCase {
$this->assertNull($data['pagination']['lastReadPostId']);
}
public function testByThreadPaginatedRespectsExplicitPageParameter(): void {
public function testByThreadRespectsExplicitPageParameter(): void {
$threadId = 1;
$perPage = 20;
@@ -1354,7 +1313,7 @@ class PostControllerTest extends TestCase {
]);
// Execute with explicit page=2
$response = $this->controller->byThreadPaginated($threadId, 2, $perPage);
$response = $this->controller->byThread($threadId, 2, $perPage);
// Assert
$this->assertEquals(Http::STATUS_OK, $response->getStatus());

View File

@@ -160,15 +160,20 @@ class ThreadControllerTest extends TestCase {
public function testByCategoryReturnsThreadsSuccessfully(): void {
$categoryId = 1;
$limit = 50;
$offset = 0;
$page = 1;
$perPage = 20;
$thread1 = $this->createMockThread(1, $categoryId, 'user1', 'Test Thread 1');
$thread2 = $this->createMockThread(2, $categoryId, 'user2', 'Test Thread 2');
$this->threadMapper->expects($this->once())
->method('countByCategoryId')
->with($categoryId)
->willReturn(2);
$this->threadMapper->expects($this->once())
->method('findByCategoryId')
->with($categoryId, $limit, $offset)
->with($categoryId, $perPage, 0)
->willReturn([$thread1, $thread2]);
$this->userService->expects($this->once())
@@ -178,12 +183,17 @@ class ThreadControllerTest extends TestCase {
'user2' => ['userId' => 'user2', 'displayName' => 'User 2'],
]);
$response = $this->controller->byCategory($categoryId, $limit, $offset);
$response = $this->controller->byCategory($categoryId, $page, $perPage);
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
$data = $response->getData();
$this->assertIsArray($data);
$this->assertCount(2, $data);
$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 testShowReturnsThreadAndIncrementsViewCount(): void {
@@ -1141,7 +1151,7 @@ class ThreadControllerTest extends TestCase {
'user2' => ['userId' => 'user2', 'displayName' => 'User 2'],
]);
$response = $this->controller->byCategoryPaginated($categoryId, $page, $perPage);
$response = $this->controller->byCategory($categoryId, $page, $perPage);
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
$data = $response->getData();
@@ -1172,7 +1182,7 @@ class ThreadControllerTest extends TestCase {
$this->userService->method('enrichMultipleUsers')->willReturn([]);
$response = $this->controller->byCategoryPaginated($categoryId, $page, $perPage);
$response = $this->controller->byCategory($categoryId, $page, $perPage);
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
$data = $response->getData();
@@ -1198,7 +1208,7 @@ class ThreadControllerTest extends TestCase {
$this->userService->method('enrichMultipleUsers')->willReturn([]);
$response = $this->controller->byCategoryPaginated($categoryId, $page, $perPage);
$response = $this->controller->byCategory($categoryId, $page, $perPage);
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
$data = $response->getData();
@@ -1223,7 +1233,7 @@ class ThreadControllerTest extends TestCase {
$this->userService->method('enrichMultipleUsers')->willReturn([]);
$response = $this->controller->byCategoryPaginated($categoryId, 5, $perPage);
$response = $this->controller->byCategory($categoryId, 5, $perPage);
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
$data = $response->getData();
@@ -1248,7 +1258,7 @@ class ThreadControllerTest extends TestCase {
$this->userService->method('enrichMultipleUsers')->willReturn([]);
$response = $this->controller->byCategoryPaginated($categoryId, $page, $perPage);
$response = $this->controller->byCategory($categoryId, $page, $perPage);
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
$data = $response->getData();
@@ -1285,7 +1295,7 @@ class ThreadControllerTest extends TestCase {
'user2' => ['userId' => 'user2', 'displayName' => 'User 2'],
]);
$response = $this->controller->byCategoryPaginated($categoryId, $page, $perPage);
$response = $this->controller->byCategory($categoryId, $page, $perPage);
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
$data = $response->getData();
@@ -1323,7 +1333,7 @@ class ThreadControllerTest extends TestCase {
'user1' => ['userId' => 'user1', 'displayName' => 'User 1'],
]);
$response = $this->controller->byCategoryPaginated($categoryId, $page, $perPage);
$response = $this->controller->byCategory($categoryId, $page, $perPage);
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
$data = $response->getData();