Skip to content

Commit

Permalink
fix: lock code row for update (#194)
Browse files Browse the repository at this point in the history
  • Loading branch information
h4l-yup committed Feb 21, 2024
1 parent 4212a05 commit dc3cc65
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 17 deletions.
9 changes: 2 additions & 7 deletions apps/api/src/domains/admin/user/user-password.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { getRepositoryToken } from '@nestjs/typeorm';
import * as bcrypt from 'bcrypt';
import type { Repository } from 'typeorm';

import { CodeTypeEnum } from '@/shared/code/code-type.enum';
import { CodeEntity } from '@/shared/code/code.entity';
import { ResetPasswordMailingService } from '@/shared/mailing/reset-password-mailing.service';
import { TestConfig } from '@/test-utils/util-functions';
Expand Down Expand Up @@ -72,16 +71,12 @@ describe('UserPasswordService', () => {
dto.code = faker.string.sample();
dto.password = faker.internet.password();
jest
.spyOn(codeRepo, 'findOneBy')
.spyOn(codeRepo, 'findOne')
.mockResolvedValue({ code: dto.code } as CodeEntity);

const user = await userPasswordService.resetPassword(dto);

expect(codeRepo.findOneBy).toHaveBeenCalledTimes(1);
expect(codeRepo.findOneBy).toHaveBeenCalledWith({
key: dto.email,
type: CodeTypeEnum.RESET_PASSWORD,
});
expect(codeRepo.findOne).toHaveBeenCalledTimes(1);
expect(bcrypt.compareSync(dto.password, user.hashPassword)).toBe(true);
});
it('resetting a password fails with an invalid email', async () => {
Expand Down
14 changes: 5 additions & 9 deletions apps/api/src/shared/code/code.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,22 +144,18 @@ describe('CodeService', () => {
});
it('verifying code succeeds with a valid code, key, type', async () => {
const { code, type } = codeEntity;
jest.spyOn(codeRepo, 'findOneBy').mockResolvedValue(codeEntity);
jest.spyOn(codeRepo, 'findOne').mockResolvedValue(codeEntity);

await codeService.verifyCode({ code, key, type });

expect(codeRepo.findOneBy).toHaveBeenCalledWith({
key: codeEntity.key,
type: codeEntity.type,
});
expect(codeRepo.save).toHaveBeenCalledWith(
Object.assign(codeEntity, { isVerified: true }),
);
});
it('verifying code fails with an invalid code', async () => {
const { type } = codeEntity;
const invalidCode = faker.string.sample(6);
jest.spyOn(codeRepo, 'findOneBy').mockResolvedValue(codeEntity);
jest.spyOn(codeRepo, 'findOne').mockResolvedValue(codeEntity);

const { error } = await codeService.verifyCode({
code: invalidCode,
Expand All @@ -172,7 +168,7 @@ describe('CodeService', () => {
const { type } = codeEntity;
const invalidCode = faker.string.sample(6);
jest
.spyOn(codeRepo, 'findOneBy')
.spyOn(codeRepo, 'findOne')
.mockResolvedValue({ ...codeEntity, tryCount: 5 } as CodeEntity);

const { error } = await codeService.verifyCode({
Expand All @@ -185,7 +181,7 @@ describe('CodeService', () => {
it('verifying code fails with an invalid key', async () => {
const { code, type } = codeEntity;
const invalidKey = faker.string.sample(6);
jest.spyOn(codeRepo, 'findOneBy').mockResolvedValue(null);
jest.spyOn(codeRepo, 'findOne').mockResolvedValue(null);

const { error } = await codeService.verifyCode({
code,
Expand All @@ -197,7 +193,7 @@ describe('CodeService', () => {
it('verifying code fails at expired date', async () => {
MockDate.set(new Date(Date.now() + 5 * 60 * 1000 + 1000));
const { code, type } = codeEntity;
jest.spyOn(codeRepo, 'findOneBy').mockResolvedValue(codeEntity);
jest.spyOn(codeRepo, 'findOne').mockResolvedValue(codeEntity);

const { error } = await codeService.verifyCode({ code, key, type });
expect(error).toEqual(new BadRequestException('code expired'));
Expand Down
5 changes: 4 additions & 1 deletion apps/api/src/shared/code/code.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ export class CodeService {

@Transactional()
async verifyCode({ code, key, type }: VerifyCodeDto) {
const codeEntity = await this.codeRepo.findOneBy({ key, type });
const codeEntity = await this.codeRepo.findOne({
where: { key, type },
lock: { mode: 'pessimistic_write' },
});

if (!codeEntity) return { error: new NotFoundException('not found code') };
if (codeEntity.isVerified)
Expand Down

0 comments on commit dc3cc65

Please sign in to comment.