diff --git a/api/src/infrastructure/repositories/postgresql/reply.repository.ts b/api/src/infrastructure/repositories/postgresql/reply.repository.ts new file mode 100644 index 00000000..e16bf4dd --- /dev/null +++ b/api/src/infrastructure/repositories/postgresql/reply.repository.ts @@ -0,0 +1,42 @@ +import { Reply } from '@src/core/entities/review.entity'; +import { + CreateReplyParams, + ReplyRepository +} from '@src/core/ports/reply.repository'; +import { AppErrorCode, CustomError } from '@src/error/errors'; +import { + ExtendedPrismaClient, + ExtendedPrismaTransactionClient +} from '@src/infrastructure/prisma/types'; + +export class PostgresqlReplyRepository implements Partial { + constructor(private readonly client: ExtendedPrismaClient) {} + + public create = async ( + params: CreateReplyParams, + txClient?: ExtendedPrismaTransactionClient + ): Promise => { + const createdAt = new Date(); + try { + const client = txClient ?? this.client; + const reply = await client.reply.create({ + data: { + reviewId: params.reviewId, + userId: params.userId, + content: params.content, + createdAt, + updatedAt: createdAt + } + }); + + return reply.convertToEntity(); + } catch (error: unknown) { + throw new CustomError({ + code: AppErrorCode.INTERNAL_ERROR, + cause: error, + message: 'failed to create reply', + context: { params } + }); + } + }; +} diff --git a/api/test/infrastructure/repositories/postgresql/reply.repository.test.ts b/api/test/infrastructure/repositories/postgresql/reply.repository.test.ts new file mode 100644 index 00000000..cf92f3f3 --- /dev/null +++ b/api/test/infrastructure/repositories/postgresql/reply.repository.test.ts @@ -0,0 +1,95 @@ +import { AccessLevel, Idp } from '@prisma/client'; +import { randomUUID } from 'crypto'; +import seedrandom from 'seedrandom'; +import { Logger } from 'winston'; + +import { + generateUserNickname, + generateUserTag +} from '@src/core/nickname.generator'; +import { + CreateReplyParams, +} from '@src/core/ports/reply.repository'; +import { generatePrismaClient } from '@src/infrastructure/prisma/prisma.client'; +import { ExtendedPrismaClient } from '@src/infrastructure/prisma/types'; +import { PostgresqlReplyRepository } from '@src/infrastructure/repositories/postgresql/reply.repository'; + +function generateRandomNumber(userId: string): number { + const rand = seedrandom(userId); + return Math.floor(rand() * 100000); +} + +describe('Test reply repository', () => { + const logger: Partial = { + error: jest.fn() + }; + let prismaClient: ExtendedPrismaClient; + let replyRepository: PostgresqlReplyRepository; + + beforeAll(() => { + const config = { + host: '127.0.0.1', + port: 5435, + user: 'mrc-client', + password: 'Client123!' + }; + prismaClient = generatePrismaClient(logger as Logger, config); + replyRepository = new PostgresqlReplyRepository(prismaClient); + }); + + afterAll(async () => { + await prismaClient.$disconnect(); + }); + + describe('Test create', () => { + const userId = randomUUID(); + const reviewId = generateRandomNumber(userId); + const content = 'randomContent'; + const createdAt = new Date(); + + beforeAll(async () => { + await prismaClient.user.create({ + data: { + id: userId, + nickname: generateUserNickname(userId), + tag: generateUserTag(userId), + idp: Idp.GOOGLE, + email: `${userId}@gmail.com`, + accessLevel: AccessLevel.USER, + createdAt, + updatedAt: createdAt + } + }); + await prismaClient.review.create({ + data: { + id: reviewId, + userId, + title: 'randomTitle', + movieName: 'randomMovieName', + content, + createdAt, + updatedAt: createdAt + } + }); + }); + + afterAll(async () => { + await prismaClient.review.delete({ where: { id: reviewId } }); + await prismaClient.user.delete({ where: { id: userId } }); + }); + + it('should success when valid', async () => { + const params: CreateReplyParams = { + reviewId, + userId, + content + }; + + const replyCreated = await replyRepository.create(params); + + expect(replyCreated.getData()).toEqual( + expect.objectContaining({ reviewId, userId, content }) + ); + }); + }); +});