Skip to content

Commit

Permalink
Merge branch 'dev' into nacho-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
na-cho-dev authored Mar 2, 2025
2 parents fa5c698 + 5c1fd59 commit 63455f0
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 7 deletions.
16 changes: 14 additions & 2 deletions src/modules/comments/comments.controller.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { UserPayload } from './../user/interfaces/user-payload.interface';
import { User } from './../user/entities/user.entity';
import { Controller, Body, Post, Request, Get, Param, Delete } from '@nestjs/common';
import { CommentsService } from './comments.service';
import { CreateCommentDto } from './dtos/create-comment.dto';
Expand All @@ -15,7 +17,7 @@ export class CommentsController {
@ApiResponse({ status: 400, description: 'Bad Request.' })
@ApiResponse({ status: 500, description: 'Internal Server Error.' })
async addComment(@Body() createCommentDto: CreateCommentDto, @Request() req): Promise<CommentResponseDto> {
const { userId } = req.user;
const userId = req.user.id;
return await this.commentsService.addComment(createCommentDto, userId);
}

Expand All @@ -26,10 +28,20 @@ export class CommentsController {
return await this.commentsService.getAComment(id);
}

@ApiOperation({ summary: 'Dislike a comment' })
@ApiResponse({ status: 200, description: 'Dislike updated successfully' })
@ApiResponse({ status: 404, description: 'Comment not found' })
@Post(':id/dislike')
async dislikeComment(@Param('id') id: string, @Request() req) {
const userId = req.user.id;
// console.log('User ID:', userId); debug
return await this.commentsService.dislikeComment(id, userId);
}

@ApiOperation({ summary: 'Delete a comment' })
@ApiResponse({ status: 200, description: 'The comment has been deleted successfully.' })
@Delete(':id/delete')
async deleteAComment(@Param('id') id: string, @Request() req): Promise<any> {
return await this.commentsService.deleteAComment(id, req.user.userId);
return await this.commentsService.deleteAComment(id, req.user.id);
}
}
33 changes: 32 additions & 1 deletion src/modules/comments/comments.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,43 @@ export class CommentsService {
throw new CustomHttpException('You are not authorized to delete this comment', HttpStatus.FORBIDDEN);
}

await this.commentRepository.delete(comment);
await this.commentRepository.delete(comment.id);

return {
message: 'Comment deleted successfully!',
status: HttpStatus.OK,
data: { comment },
};
}

async dislikeComment(commentId: string, userId: string): Promise<{ message: string; dislikeCount: number }> {
const comment = await this.commentRepository
.createQueryBuilder('comment')
.where('comment.id = :id', { id: commentId })
.getOne();

if (!comment) {
throw new CustomHttpException('Comment not found', HttpStatus.NOT_FOUND);
}

if (!comment.dislikedBy) {
comment.dislikedBy = [];
}

// Check if the user has already disliked the comment
if (comment.dislikedBy.includes(userId)) {
throw new CustomHttpException('You have already disliked this comment', HttpStatus.BAD_REQUEST);
}

// Add the user to the dislikedBy array and increment dislikes
comment.dislikedBy.push(userId);
comment.dislikes = comment.dislikedBy.length;

await this.commentRepository.save(comment);

return {
message: 'Dislike updated successfully',
dislikeCount: comment.dislikes,
};
}
}
9 changes: 9 additions & 0 deletions src/modules/comments/dtos/dislike-comment.dto
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsUUID } from 'class-validator';

export class DislikeCommentDto {
@ApiProperty({ description: 'Comment ID to dislike' })
@IsNotEmpty()
@IsUUID()
commentId: string;
}
6 changes: 6 additions & 0 deletions src/modules/comments/entities/comments.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@ export class Comment extends AbstractBaseEntity {

@Column({ nullable: true })
model_type: string;

@Column({ type: 'int', default: 0 }) // Add dislikes column
dislikes: number;

@Column('simple-array', { nullable: true }) // Store user IDs as an array
dislikedBy: string[];
}
49 changes: 45 additions & 4 deletions src/modules/comments/tests/comments.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ import { User } from '../../user/entities/user.entity';
import { CustomHttpException } from '@shared/helpers/custom-http-filter';
import { HttpStatus } from '@nestjs/common';

const mockQueryBuilder = {
where: jest.fn().mockReturnThis(),
getOne: jest.fn(),
};

const mockCommentRepository = () => ({
create: jest.fn(),
save: jest.fn(),
findOne: jest.fn(),
delete: jest.fn(),
createQueryBuilder: jest.fn(() => mockQueryBuilder),
});

const mockUserRepository = () => ({
Expand Down Expand Up @@ -105,24 +111,24 @@ describe('CommentsService', () => {
it('should delete a comment successfully', async () => {
const commentId = 'comment-id';
const userId = 'user-id';
const mockUser = { id: 'user-id' };
const mockUser = { id: userId };
const mockComment = {
id: 'comment-id',
id: commentId,
model_id: '1',
model_type: 'post',
comment: 'A valid comment',
user: mockUser,
};

commentRepository.findOne.mockResolvedValue(mockComment);
commentRepository.delete.mockResolvedValue(mockComment);
commentRepository.delete.mockResolvedValue({ affected: 1 });

console.log(await commentRepository.findOne({ where: { id: commentId }, relations: ['user'] })); // Debugging

const result = await service.deleteAComment(commentId, userId);

expect(commentRepository.findOne).toHaveBeenCalledWith({ where: { id: commentId }, relations: ['user'] });
expect(commentRepository.delete).toHaveBeenCalledWith(mockComment);
expect(commentRepository.delete).toHaveBeenCalledWith(commentId);
expect(result).toEqual({
message: 'Comment deleted successfully!',
status: HttpStatus.OK,
Expand All @@ -131,4 +137,39 @@ describe('CommentsService', () => {
});
});
});

describe('CommentsService - dislikeComment', () => {
it('should throw CustomHttpException if comment is not found', async () => {
commentRepository.findOne.mockResolvedValue(null);

await expect(service.dislikeComment('comment-id', 'user-id')).rejects.toThrow(CustomHttpException);
await expect(service.dislikeComment('comment-id', 'user-id')).rejects.toMatchObject({
message: 'Comment not found',
status: HttpStatus.NOT_FOUND,
});
});

it('should increase the dislike count successfully', async () => {
const mockComment = {
id: 'comment-id',
dislikes: 2,
dislikedBy: ['user1', 'user2'], // Ensure this is initialized
};

// Mock `getOne()` from `createQueryBuilder`
mockQueryBuilder.getOne.mockResolvedValue(mockComment);
commentRepository.createQueryBuilder.mockReturnValue(mockQueryBuilder);

commentRepository.save.mockResolvedValue({ ...mockComment, dislikes: 3 });

const result = await service.dislikeComment('comment-id', 'user-id');

expect(result).toEqual({
message: 'Dislike updated successfully',
dislikeCount: 3,
});

expect(commentRepository.save).toHaveBeenCalledWith({ ...mockComment, dislikes: 3 });
});
});
});
2 changes: 2 additions & 0 deletions src/modules/products/tests/mocks/comment.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export const mockComment: Comment = {
model_type: 'Product',
user: mockUser,
product: productMock,
dislikes: 0,
dislikedBy: [],
created_at: new Date(),
updated_at: new Date(),
};

0 comments on commit 63455f0

Please sign in to comment.