Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

만료 초대 코드 삭제, 테스트 추가 #366

Merged
merged 8 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions nestjs-BE/server/src/invite-codes/invite-codes.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { GoneException, HttpStatus, NotFoundException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { Space } from '@prisma/client';
import { InviteCodesController } from './invite-codes.controller';
import { InviteCodesService } from './invite-codes.service';

describe('InviteCodesController', () => {
let controller: InviteCodesController;
let inviteCodesService: InviteCodesService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [InviteCodesController],
providers: [{ provide: InviteCodesService, useValue: {} }],
}).compile();

controller = module.get<InviteCodesController>(InviteCodesController);
inviteCodesService = module.get<InviteCodesService>(InviteCodesService);
});

describe('findSpace', () => {
const testInviteCode = 'test invite code';
const testSpace: Space = {
uuid: 'test uuid',
name: 'test space',
icon: 'test icon',
};

beforeEach(() => {
(inviteCodesService.findSpace as jest.Mock) = jest.fn(
async () => testSpace,
);
});

it('success', async () => {
const space = controller.findSpace(testInviteCode);

await expect(space).resolves.toEqual({
statusCode: HttpStatus.OK,
message: 'Success',
data: testSpace,
});
});

it('invite code not found', async () => {
(inviteCodesService.findSpace as jest.Mock).mockRejectedValue(
new NotFoundException(),
);

const space = controller.findSpace(testInviteCode);

await expect(space).rejects.toThrow(NotFoundException);
});

it('invite code expired', async () => {
(inviteCodesService.findSpace as jest.Mock).mockRejectedValue(
new GoneException(),
);

const space = controller.findSpace(testInviteCode);

await expect(space).rejects.toThrow(GoneException);
});
});
});
121 changes: 121 additions & 0 deletions nestjs-BE/server/src/invite-codes/invite-codes.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { Test, TestingModule } from '@nestjs/testing';
import { GoneException, NotFoundException } from '@nestjs/common';
import { InviteCode, Space } from '@prisma/client';
import { InviteCodesService } from './invite-codes.service';
import { SpacesService } from '../spaces/spaces.service';
import { PrismaService } from '../prisma/prisma.service';
import * as ExpiryModule from '../utils/date';

describe('InviteCodesService', () => {
let inviteCodesService: InviteCodesService;
let prisma: PrismaService;
let spacesService: SpacesService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
InviteCodesService,
{
provide: PrismaService,
useValue: {
inviteCode: {
create: jest.fn(),
},
},
},
{
provide: SpacesService,
useValue: {
findSpaceBySpaceUuid: jest.fn(),
},
},
],
}).compile();

inviteCodesService = module.get<InviteCodesService>(InviteCodesService);
prisma = module.get<PrismaService>(PrismaService);
spacesService = module.get<SpacesService>(SpacesService);
});

describe('findSpace', () => {
const testSpace: Space = {
uuid: 'test uuid',
name: 'test space',
icon: 'test icon',
};
const testInviteCode = 'test invite code';
const testInviteCodeDate = {
spaceUuid: 'space uuid',
expiryDate: 'expriy date',
} as unknown as InviteCode;
let checkExpirySpy: jest.SpyInstance;
let deleteInviteCodeSpy: jest.SpyInstance;

beforeEach(() => {
jest
.spyOn(inviteCodesService, 'findInviteCode')
.mockResolvedValue(testInviteCodeDate);
checkExpirySpy = jest
.spyOn(ExpiryModule, 'checkExpiry')
.mockReturnValue(false);
deleteInviteCodeSpy = jest
.spyOn(inviteCodesService, 'deleteInviteCode')
.mockResolvedValue(null);
(spacesService.findSpaceBySpaceUuid as jest.Mock).mockResolvedValue(
testSpace,
);
});

afterEach(() => {
checkExpirySpy.mockRestore();
});

it('success', async () => {
const space = inviteCodesService.findSpace(testInviteCode);

await expect(space).resolves.toEqual(testSpace);
expect(deleteInviteCodeSpy).not.toHaveBeenCalled();
});

it('invite code not found', async () => {
jest.spyOn(inviteCodesService, 'findInviteCode').mockResolvedValue(null);

const space = inviteCodesService.findSpace(testInviteCode);

await expect(space).rejects.toThrow(NotFoundException);
expect(checkExpirySpy).not.toHaveBeenCalled();
});

it('invite code expired', async () => {
checkExpirySpy.mockReturnValue(true);

const space = inviteCodesService.findSpace(testInviteCode);

await expect(space).rejects.toThrow(GoneException);
expect(deleteInviteCodeSpy).toHaveBeenCalled();
});
});

describe('createInviteCode', () => {
const testSpace: Space = {
uuid: 'test uuid',
name: 'test space',
icon: 'test icon',
};

beforeEach(() => {
(spacesService.findSpaceBySpaceUuid as jest.Mock).mockResolvedValue(
testSpace,
);
});

it('space not found', async () => {
(spacesService.findSpaceBySpaceUuid as jest.Mock).mockResolvedValue(null);

const inviteCode = inviteCodesService.createInviteCode(testSpace.uuid);

await expect(inviteCode).rejects.toThrow(NotFoundException);
expect(prisma.inviteCode.create).not.toHaveBeenCalled();
});
});
});
39 changes: 22 additions & 17 deletions nestjs-BE/server/src/invite-codes/invite-codes.service.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import {
HttpException,
HttpStatus,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { GoneException, Injectable, NotFoundException } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { InviteCode, Prisma } from '@prisma/client';
import { v4 as uuid } from 'uuid';
import { PrismaService } from '../prisma/prisma.service';
import {
INVITE_CODE_EXPIRY_HOURS,
INVITE_CODE_LENGTH,
} from '../config/magic-number';
import generateUuid from '../utils/uuid';
import { checkExpiry, getExpiryDate } from '../utils/date';
import { SpacesService } from '../spaces/spaces.service';

Expand All @@ -31,8 +27,8 @@ export class InviteCodesService {
const inviteCodeData = await this.findInviteCode(inviteCode);
if (!inviteCodeData) throw new NotFoundException();
if (checkExpiry(inviteCodeData.expiryDate)) {
this.deleteInviteCode(inviteCode);
throw new HttpException('Invite code has expired.', HttpStatus.GONE);
await this.deleteInviteCode(inviteCode);
throw new GoneException('Invite code has expired.');
}
return this.spacesService.findSpaceBySpaceUuid(inviteCodeData.spaceUuid);
}
Expand All @@ -42,14 +38,21 @@ export class InviteCodesService {
if (!space) throw new NotFoundException();
return this.prisma.inviteCode.create({
data: {
uuid: generateUuid(),
uuid: uuid(),
inviteCode: await this.generateUniqueInviteCode(INVITE_CODE_LENGTH),
spaceUuid: spaceUuid,
expiryDate: getExpiryDate({ hour: INVITE_CODE_EXPIRY_HOURS }),
},
});
}

@Cron(CronExpression.EVERY_HOUR)
async deleteExpiredInviteCode() {
await this.prisma.inviteCode.deleteMany({
where: { expiryDate: { lt: new Date() } },
});
}

async deleteInviteCode(inviteCode: string): Promise<InviteCode | null> {
try {
return await this.prisma.inviteCode.delete({
Expand Down Expand Up @@ -79,14 +82,16 @@ export class InviteCodesService {
}

private async generateUniqueInviteCode(length: number): Promise<string> {
let inviteCode: string;
let inviteCodeData: InviteCode | null;
return this.prisma.$transaction(async () => {
let inviteCode: string;
let inviteCodeData: InviteCode | null;

do {
inviteCode = this.generateShortInviteCode(length);
inviteCodeData = await this.findInviteCode(inviteCode);
} while (inviteCodeData !== null);
do {
inviteCode = this.generateShortInviteCode(length);
inviteCodeData = await this.findInviteCode(inviteCode);
} while (inviteCodeData !== null);

return inviteCode;
return inviteCode;
});
}
}
6 changes: 0 additions & 6 deletions nestjs-BE/server/src/utils/uuid.ts

This file was deleted.