Skip to content

Commit

Permalink
feat: implement delete review service method
Browse files Browse the repository at this point in the history
  • Loading branch information
skgndi12 committed Mar 14, 2024
1 parent cf6f36f commit e03b7fa
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
34 changes: 34 additions & 0 deletions api/src/core/services/review/review.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { UserRepository } from '@src/core/ports/user.repository';
import {
CreateReviewDto,
CreateReviewResponse,
DeleteReviewDto,
GetReviewResponse,
GetReviewsDto,
GetReviewsResponse,
Expand Down Expand Up @@ -207,6 +208,39 @@ export class ReviewService {
};
};

public deleteReview = async (dto: DeleteReviewDto): Promise<null> => {
return await this.txManager.runInTransaction(
async (txClient: TransactionClient): Promise<null> => {
const reviewToDelete = await this.reviewRepository.findById(
dto.reviewId,
txClient
);
const userReviewing = await this.userRepository.findById(
reviewToDelete.userId,
txClient
);

if (
!dto.requesterIdToken.isAccessLevelAndUserIdAuthorized(
new AccessLevelEnum(AccessLevel.DEVELOPER),
userReviewing.id
)
) {
throw new CustomError({
code: AppErrorCode.PERMISSIION_DENIED,
message: 'insufficient access level to delete review',
context: { dto, review: reviewToDelete, user: userReviewing }
});
}

await this.reviewRepository.deleteById(dto.reviewId, txClient);

return null;
},
IsolationLevel.READ_COMMITTED
);
};

private extractUserIds = (entries: Review[] | Reply[]): string[] => {
const userIds: string[] = [];
entries.forEach((entry) => {
Expand Down
5 changes: 5 additions & 0 deletions api/src/core/services/review/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,8 @@ export interface UpdateReviewDto {
movieName: string;
content: string;
}

export interface DeleteReviewDto {
requesterIdToken: AppIdToken;
reviewId: number;
}
124 changes: 124 additions & 0 deletions api/test/core/services/review/review.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { UserRepository } from '@src/core/ports/user.repository';
import { ReviewService } from '@src/core/services/review/review.service';
import {
CreateReviewDto,
DeleteReviewDto,
GetReviewsDto,
UpdateReviewDto
} from '@src/core/services/review/types';
Expand Down Expand Up @@ -597,4 +598,127 @@ describe('Test review service', () => {
expect(reviewRepository.update).toBeCalledTimes(0);
});
});

describe('Test delete review', () => {
const userId = 'randomId';
const nickname = 'randomNickname';
const tag = '#TAGG';
const idp = new IdpEnum(Idp.GOOGLE);
const email = '[email protected]';
const accessLevel = new AccessLevelEnum(AccessLevel.USER);
const requesterIdToken = new AppIdToken(
userId,
nickname,
tag,
idp,
email,
accessLevel
);
const reviewId = 0;
const title = 'randomTitle';
const movieName = 'randomMovie';
const content = 'randomContent';
const createdAt = new Date();

const userFound = new User(
userId,
nickname,
tag,
idp,
email,
accessLevel,
createdAt,
createdAt
);
const reviewFound = new Review(
reviewId,
userId,
title,
movieName,
content,
0,
createdAt,
createdAt
);

const userFindById = jest.fn(() => Promise.resolve(userFound)) as jest.Mock;
const reviewFindById = jest.fn(() =>
Promise.resolve(reviewFound)
) as jest.Mock;
const reviewDeleteById = jest.fn(() => Promise.resolve()) as jest.Mock;

beforeAll(() => {
prismaMock.$transaction.mockImplementation((callback) =>
callback(prismaMock)
);
userRepository = new PostgresqlUserRepository(prismaMock);
reviewRepository = new PostgresqlReviewRepository(prismaMock);
replyRepository = new PostgresqlReplyRepository(prismaMock);
txManager = new PrismaTransactionManager(prismaMock);
userRepository.findById = userFindById;
reviewRepository.findById = reviewFindById;
reviewRepository.deleteById = reviewDeleteById;
});

it('should success when valid', async () => {
const givenDto: DeleteReviewDto = {
requesterIdToken,
reviewId
};
await new ReviewService(
userRepository,
reviewRepository,
replyRepository,
txManager
).deleteReview(givenDto);

expect(reviewRepository.findById).toBeCalledTimes(1);
const reviewFindByIdArgs = reviewFindById.mock.calls[0][0];
expect(reviewFindByIdArgs).toEqual(givenDto.reviewId);

expect(userRepository.findById).toBeCalledTimes(1);
const userFindByIdArgs = userFindById.mock.calls[0][0];
expect(userFindByIdArgs).toEqual(reviewFound.userId);

expect(reviewRepository.deleteById).toBeCalledTimes(1);
const reviewDeleteByIdArgs = reviewDeleteById.mock.calls[0][0];
expect(reviewDeleteByIdArgs).toEqual(givenDto.reviewId);
});

it('should fail when access level and user authorization are invalid', async () => {
const givenRequesterIdToken = new AppIdToken(
'anotherRandomId',
'anotherNickname',
'#GGAT',
new IdpEnum(Idp.GOOGLE),
'[email protected]',
new AccessLevelEnum(AccessLevel.USER)
);
const givenDto: DeleteReviewDto = {
requesterIdToken: givenRequesterIdToken,
reviewId
};
try {
await new ReviewService(
userRepository,
reviewRepository,
replyRepository,
txManager
).deleteReview(givenDto);
} catch (error: unknown) {
expect(error).toBeInstanceOf(CustomError);
expect(error).toHaveProperty('code', AppErrorCode.PERMISSIION_DENIED);
}

expect(reviewRepository.findById).toBeCalledTimes(1);
const reviewFindByIdArgs = reviewFindById.mock.calls[0][0];
expect(reviewFindByIdArgs).toEqual(givenDto.reviewId);

expect(userRepository.findById).toBeCalledTimes(1);
const userFindByIdArgs = userFindById.mock.calls[0][0];
expect(userFindByIdArgs).toEqual(reviewFound.userId);

expect(reviewRepository.deleteById).toBeCalledTimes(0);
});
});
});

0 comments on commit e03b7fa

Please sign in to comment.