From 35440eb30530f01f89c8ed1829fea127fd448afd Mon Sep 17 00:00:00 2001 From: jeongmin Date: Thu, 7 Dec 2023 18:20:48 +0900 Subject: [PATCH 01/18] =?UTF-8?q?test:=20typeorm-transactional-tests=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 모킹 제거 --- BE/package-lock.json | 50 +++++++++++++---------- BE/package.json | 1 + BE/test/int/purchase.service.int-spec.ts | 51 +++++------------------- 3 files changed, 40 insertions(+), 62 deletions(-) diff --git a/BE/package-lock.json b/BE/package-lock.json index e715395..766ab73 100644 --- a/BE/package-lock.json +++ b/BE/package-lock.json @@ -59,6 +59,7 @@ "ts-loader": "^9.4.3", "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", + "typeorm-transactional-tests": "^2.0.0", "typescript": "^5.1.3" } }, @@ -3049,14 +3050,6 @@ } ] }, - "node_modules/base64url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", @@ -7489,19 +7482,6 @@ "pkginfo": "~0.3.0" } }, - "node_modules/passport-oauth2": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz", - "integrity": "sha512-wpsGtJDHHQUjyc9WcV9FFB0bphFExpmKtzkQrxpH1vnSr6RcWa3ZEGHx/zGKAh2PN7Po9TKYB1fJeOiIBspNPA==", - "dependencies": { - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/passport-naver": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/passport-naver/-/passport-naver-1.0.6.tgz", @@ -7540,6 +7520,19 @@ "url": "https://github.com/sponsors/jaredhanson" } }, + "node_modules/passport-oauth2": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz", + "integrity": "sha512-wpsGtJDHHQUjyc9WcV9FFB0bphFExpmKtzkQrxpH1vnSr6RcWa3ZEGHx/zGKAh2PN7Po9TKYB1fJeOiIBspNPA==", + "dependencies": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", @@ -9393,6 +9386,21 @@ } } }, + "node_modules/typeorm-transactional-tests": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/typeorm-transactional-tests/-/typeorm-transactional-tests-2.0.0.tgz", + "integrity": "sha512-xfeEvWjYjaVU0TM4q0CV0jPznBPCZoAubqLWvtKc+RxrvyCfjexWI23mq+QiUdOHQ8f5UTJktuYh9nyTYbvSWQ==", + "dev": true, + "engines": { + "node": ">=6.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + }, + "peerDependencies": { + "typeorm": "^0.3.6" + } + }, "node_modules/typeorm/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", diff --git a/BE/package.json b/BE/package.json index 647d01d..29a71bb 100644 --- a/BE/package.json +++ b/BE/package.json @@ -72,6 +72,7 @@ "ts-loader": "^9.4.3", "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", + "typeorm-transactional-tests": "^2.0.0", "typescript": "^5.1.3" }, "jest": { diff --git a/BE/test/int/purchase.service.int-spec.ts b/BE/test/int/purchase.service.int-spec.ts index 7e7d2af..59b519b 100644 --- a/BE/test/int/purchase.service.int-spec.ts +++ b/BE/test/int/purchase.service.int-spec.ts @@ -4,16 +4,16 @@ import { User } from "src/auth/users.entity"; import { UsersRepository } from "src/auth/users.repository"; import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { PurchaseDesignDto } from "src/purchase/dto/purchase.design.dto"; -import { Purchase } from "src/purchase/purchase.entity"; import { PurchaseRepository } from "src/purchase/purchase.repository"; import { PurchaseService } from "src/purchase/purchase.service"; import { premiumStatus } from "src/utils/enum"; import { DataSource, QueryRunner } from "typeorm"; +import { TransactionalTestContext } from "typeorm-transactional-tests"; describe("PurchaseService 통합 테스트", () => { let purchaseService: PurchaseService; let dataSource: DataSource; - let queryRunner: QueryRunner; + let transactionalContext: TransactionalTestContext; const userMockData = { userId: "PurchaseServiceTest", @@ -30,22 +30,18 @@ describe("PurchaseService 통합 테스트", () => { purchaseService = moduleFixture.get(PurchaseService); dataSource = moduleFixture.get(DataSource); - queryRunner = dataSource.createQueryRunner(); - await queryRunner.connect(); }); beforeEach(async () => { - await queryRunner.startTransaction(); + transactionalContext = new TransactionalTestContext(dataSource); + await transactionalContext.start(); }); afterEach(async () => { - await queryRunner.rollbackTransaction(); - jest.restoreAllMocks(); + await transactionalContext.finish(); }); - afterAll(async () => { - await queryRunner.release(); - }); + afterAll(async () => {}); // // 별가루가 부족한 경우 테스트 // // 이미 존재하는 디자인에 대한 경우 테스트 @@ -59,23 +55,8 @@ describe("PurchaseService 통합 테스트", () => { premium: premiumStatus.FALSE, credit: 500, }); - await queryRunner.manager.save(user); - - jest.spyOn(user, "save").mockImplementation(async () => { - return queryRunner.manager.save(user); - }); - const purchase = new Purchase(); - jest.spyOn(Purchase, "create").mockReturnValue(purchase); - jest.spyOn(purchase, "save").mockImplementation(async () => { - return queryRunner.manager.save(purchase); - }); - jest.spyOn(Purchase, "find").mockImplementation(async (options) => { - return queryRunner.manager.find(Purchase, options); - }); - jest.spyOn(Purchase, "findOne").mockImplementation(async (options) => { - return queryRunner.manager.findOne(Purchase, options); - }); + await user.save(); const purchaseDesignDto = new PurchaseDesignDto(); purchaseDesignDto.domain = "GROUND"; @@ -103,11 +84,7 @@ describe("PurchaseService 통합 테스트", () => { credit: 500, }); - await queryRunner.manager.save(user); - - jest.spyOn(user, "save").mockImplementation(async () => { - return queryRunner.manager.save(user); - }); + await user.save(); const result = await purchaseService.purchasePremium(user); @@ -121,11 +98,7 @@ describe("PurchaseService 통합 테스트", () => { premium: premiumStatus.FALSE, credit: 300, }); - await queryRunner.manager.save(user); - - jest.spyOn(user, "save").mockImplementation(async () => { - return queryRunner.manager.save(user); - }); + await user.save(); await expect(purchaseService.purchasePremium(user)).rejects.toThrow( `보유한 별가루가 부족합니다. 현재 ${user.credit} 별가루`, @@ -138,11 +111,7 @@ describe("PurchaseService 통합 테스트", () => { premium: premiumStatus.TRUE, credit: 500, }); - await queryRunner.manager.save(user); - - jest.spyOn(user, "save").mockImplementation(async () => { - return queryRunner.manager.save(user); - }); + await user.save(); await expect(purchaseService.purchasePremium(user)).rejects.toThrow( "이미 프리미엄 사용자입니다.", From b0ce1ee995478d86984e0e846321636829754852 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Sun, 10 Dec 2023 18:01:34 +0900 Subject: [PATCH 02/18] =?UTF-8?q?test:=20auth.service.int-spec=20=ED=8A=B8?= =?UTF-8?q?=EB=9E=9C=EC=9E=AD=EC=85=98=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 트랜잭션 모듈 추가 - 테스트 더미 데이터 활용하여 로그인 추가 - 사용하지 않는 모듈 제거 --- BE/test/int/auth.service.int-spec.ts | 63 ++++++++++++++++++---------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/BE/test/int/auth.service.int-spec.ts b/BE/test/int/auth.service.int-spec.ts index 94f55f5..edc96fe 100644 --- a/BE/test/int/auth.service.int-spec.ts +++ b/BE/test/int/auth.service.int-spec.ts @@ -7,18 +7,18 @@ import { AuthModule } from "src/auth/auth.module"; import { AuthService } from "src/auth/auth.service"; import { UsersRepository } from "src/auth/users.repository"; import { JwtService } from "@nestjs/jwt"; -import { clearUserDb } from "src/utils/clearDb"; import { CreateUserDto } from "src/auth/dto/users.dto"; import { AuthCredentialsDto } from "src/auth/dto/auth-credential.dto"; import { Request } from "express"; import { LoginResponseDto } from "src/auth/dto/login-response.dto"; import { NotFoundException } from "@nestjs/common"; -import { providerEnum } from "src/utils/enum"; +import { DataSource } from "typeorm"; +import { TransactionalTestContext } from "typeorm-transactional-tests"; describe("AuthService 통합 테스트", () => { let authService: AuthService; - let usersRepository: UsersRepository; - let jwtService: JwtService; + let dataSource: DataSource; + let transactionalContext: TransactionalTestContext; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ @@ -35,15 +35,18 @@ describe("AuthService 통합 테스트", () => { ], providers: [AuthService, UsersRepository, JwtService], }).compile(); - authService = await moduleFixture.get(AuthService); - usersRepository = await moduleFixture.get(UsersRepository); - jwtService = await moduleFixture.get(JwtService); + authService = moduleFixture.get(AuthService); + dataSource = moduleFixture.get(DataSource); + }); - await clearUserDb(moduleFixture, usersRepository); + beforeEach(async () => { + transactionalContext = new TransactionalTestContext(dataSource); + await transactionalContext.start(); }); afterEach(async () => { - await jest.clearAllMocks(); + await transactionalContext.finish(); + await jest.restoreAllMocks(); }); describe("signUp 메서드", () => { @@ -64,14 +67,16 @@ describe("AuthService 통합 테스트", () => { describe("signIn 메서드", () => { it("메서드 정상 요청", async () => { const authCredentialsDto: AuthCredentialsDto = { - userId: "commonUser", - password: process.env.COMMON_USER_PASS, + userId: "oldUser", + password: "oldUser", }; const request = { ip: "111.111.111.111" } as Request; const result = await authService.signIn(authCredentialsDto, request); expect(result).toBeInstanceOf(LoginResponseDto); + expect(result.nickname).toBe("기존유저"); + expect(result.premium).toBe("TRUE"); }); it("존재하지 않는 아이디로 요청 시 실패", async () => { @@ -124,11 +129,18 @@ describe("AuthService 통합 테스트", () => { userId: "commonUser", password: process.env.COMMON_USER_PASS, }; - const user = await User.findOne({ where: { userId: "commonUser" } }); - const request = { ip: "111.111.111.111" } as Request; + const request = { + ip: "111.111.111.111", + headers: { authorization: "" }, + } as Request; - await authService.signIn(authCredentialsDto, request); - const result = await authService.reissueAccessToken(user, request); + const { accessToken } = await authService.signIn( + authCredentialsDto, + request, + ); + + request.headers.authorization = `Bearer ${accessToken}`; + const result = await authService.reissueAccessToken(request); expect(result).toBeInstanceOf(LoginResponseDto); }); @@ -136,18 +148,25 @@ describe("AuthService 통합 테스트", () => { describe("naverSignIn 메서드", () => { it("메서드 정상 요청", async () => { - const user = new User(); - user.email = "test@naver.com"; - user.userId = "test*naver"; - user.nickname = "test"; - user.password = "test"; - user.provider = providerEnum.NAVER; - + const user = await User.findOne({ where: { userId: "naverUser" } }); const request = { ip: "111.111.111.111" } as Request; const result = await authService.naverSignIn(user, request); + expect(result).toBeInstanceOf(LoginResponseDto); + expect(result.nickname).toBe("네이버유저"); + expect(result.premium).toBe("FALSE"); + }); + }); + describe("kakaoSignIn 메서드", () => { + it("메서드 정상 요청", async () => { + const user = await User.findOne({ where: { userId: "kakaoUser" } }); + const request = { ip: "111.111.111.111" } as Request; + + const result = await authService.naverSignIn(user, request); expect(result).toBeInstanceOf(LoginResponseDto); + expect(result.nickname).toBe("카카오유저"); + expect(result.premium).toBe("TRUE"); }); }); }); From f7f10f71364059fcc975e4d65a58765ee62ee420 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Sun, 10 Dec 2023 18:57:55 +0900 Subject: [PATCH 03/18] =?UTF-8?q?test:=20=ED=86=B5=ED=95=A9=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=8A=B8=EB=9E=9C=EC=9E=AD=EC=85=98=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/test/int/diaries.repository.int-spec.ts | 15 ++- BE/test/int/diaries.service.int-spec.ts | 32 +++++- BE/test/int/purchase.repository.int-spec.ts | 19 +++- BE/test/int/purchase.service.int-spec.ts | 2 +- BE/test/int/shapes.repository.int-spec.ts | 19 +++- BE/test/int/shapes.service.int-spec.ts | 8 +- BE/test/int/stat.service.int-spec.ts | 112 +++++++++----------- BE/test/int/tags.repository.int-spec.ts | 17 ++- BE/test/int/user.repository.int-spec.ts | 17 ++- 9 files changed, 158 insertions(+), 83 deletions(-) diff --git a/BE/test/int/diaries.repository.int-spec.ts b/BE/test/int/diaries.repository.int-spec.ts index e237b60..05c5d83 100644 --- a/BE/test/int/diaries.repository.int-spec.ts +++ b/BE/test/int/diaries.repository.int-spec.ts @@ -13,11 +13,15 @@ import { Shape } from "src/shapes/shapes.entity"; import { Tag } from "src/tags/tags.entity"; import { sentimentStatus } from "src/utils/enum"; import { NotFoundException } from "@nestjs/common"; +import { DataSource } from "typeorm"; +import { TransactionalTestContext } from "typeorm-transactional-tests"; describe("DiariesRepository 통합 테스트", () => { let diariesRepository: DiariesRepository; let user: User; let createdDiary: Diary; + let dataSource: DataSource; + let transactionalContext: TransactionalTestContext; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ @@ -35,11 +39,14 @@ describe("DiariesRepository 통합 테스트", () => { providers: [DiariesRepository], }).compile(); - diariesRepository = - await moduleFixture.get(DiariesRepository); + diariesRepository = moduleFixture.get(DiariesRepository); + dataSource = moduleFixture.get(DataSource); }); beforeEach(async () => { + transactionalContext = new TransactionalTestContext(dataSource); + await transactionalContext.start(); + user = await User.findOne({ where: { userId: "commonUser" } }); const shape = await Shape.findOne({ where: { user: { userId: "commonUser" } }, @@ -70,6 +77,10 @@ describe("DiariesRepository 통합 테스트", () => { ); }); + afterEach(async () => { + await transactionalContext.finish(); + }); + describe("createDiary 통합 테스트", () => { it("메서드 정상 요청", async () => { expect(createdDiary).toBeInstanceOf(Diary); diff --git a/BE/test/int/diaries.service.int-spec.ts b/BE/test/int/diaries.service.int-spec.ts index 35939ee..3fff809 100644 --- a/BE/test/int/diaries.service.int-spec.ts +++ b/BE/test/int/diaries.service.int-spec.ts @@ -1,6 +1,6 @@ import { RedisModule } from "@liaoliaots/nestjs-redis"; import { Test, TestingModule } from "@nestjs/testing"; -import { TypeOrmModule, getRepositoryToken } from "@nestjs/typeorm"; +import { TypeOrmModule } from "@nestjs/typeorm"; import { User } from "src/auth/users.entity"; import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { Diary } from "src/diaries/diaries.entity"; @@ -22,6 +22,8 @@ import { HttpModule, HttpService } from "@nestjs/axios"; import { ReadDiaryDto } from "src/diaries/dto/diaries.read.dto"; import { createCipheriv, createDecipheriv } from "crypto"; import { of } from "rxjs"; +import { DataSource } from "typeorm"; +import { TransactionalTestContext } from "typeorm-transactional-tests"; describe("DiariesService 통합 테스트", () => { let diariesService: DiariesService; @@ -31,6 +33,8 @@ describe("DiariesService 통합 테스트", () => { let user: User; let shape: Shape; let createResult: Diary; + let dataSource: DataSource; + let transactionalContext: TransactionalTestContext; jest.mock("rxjs", () => ({ lastValueFrom: jest.fn().mockResolvedValue({ @@ -75,12 +79,16 @@ describe("DiariesService 통합 테스트", () => { ], }).compile(); - diariesService = await moduleFixture.get(DiariesService); - tagsRepository = await moduleFixture.get(TagsRepository); - httpService = await moduleFixture.get(HttpService); + diariesService = moduleFixture.get(DiariesService); + tagsRepository = moduleFixture.get(TagsRepository); + httpService = moduleFixture.get(HttpService); + dataSource = moduleFixture.get(DataSource); }); beforeEach(async () => { + transactionalContext = new TransactionalTestContext(dataSource); + await transactionalContext.start(); + sentimentDto = new SentimentDto(); sentimentDto.positiveRatio = 0; sentimentDto.negativeRatio = 0; @@ -110,13 +118,27 @@ describe("DiariesService 통합 테스트", () => { }); afterEach(async () => { - await jest.clearAllMocks(); + await transactionalContext.finish(); + await jest.restoreAllMocks(); }); describe("writeDiary 통합 테스트", () => { it("메서드 정상 요청", async () => { expect(createResult).toBeInstanceOf(Diary); expect(createResult.title).toBe("Test Title"); + expect(createResult).toMatchObject({ + title: "Test Title", + content: "f2302664806f9f519404b1d583902d36", + positiveRatio: 0, + negativeRatio: 0, + neutralRatio: 100, + sentiment: "neutral", + date: "2023-11-29", + point: "1,1,1", + user: { id: 1 }, + shape: { id: 1 }, + deletedDate: null, + }); }); }); diff --git a/BE/test/int/purchase.repository.int-spec.ts b/BE/test/int/purchase.repository.int-spec.ts index f23ee55..cf346cd 100644 --- a/BE/test/int/purchase.repository.int-spec.ts +++ b/BE/test/int/purchase.repository.int-spec.ts @@ -4,17 +4,32 @@ import { User } from "src/auth/users.entity"; import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { Purchase } from "src/purchase/purchase.entity"; import { PurchaseRepository } from "src/purchase/purchase.repository"; +import { DataSource } from "typeorm"; +import { TransactionalTestContext } from "typeorm-transactional-tests"; describe("PurchaseRepository 통합 테스트", () => { let purchaseRepository: PurchaseRepository; + let dataSource: DataSource; + let transactionalContext: TransactionalTestContext; beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ + const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [TypeOrmModule.forRoot(typeORMTestConfig)], providers: [PurchaseRepository], }).compile(); - purchaseRepository = module.get(PurchaseRepository); + purchaseRepository = + moduleFixture.get(PurchaseRepository); + dataSource = moduleFixture.get(DataSource); + }); + + beforeEach(async () => { + transactionalContext = new TransactionalTestContext(dataSource); + await transactionalContext.start(); + }); + + afterEach(async () => { + await transactionalContext.finish(); }); describe("getDesignPurchaseList 메서드", () => { diff --git a/BE/test/int/purchase.service.int-spec.ts b/BE/test/int/purchase.service.int-spec.ts index 59b519b..b403870 100644 --- a/BE/test/int/purchase.service.int-spec.ts +++ b/BE/test/int/purchase.service.int-spec.ts @@ -7,7 +7,7 @@ import { PurchaseDesignDto } from "src/purchase/dto/purchase.design.dto"; import { PurchaseRepository } from "src/purchase/purchase.repository"; import { PurchaseService } from "src/purchase/purchase.service"; import { premiumStatus } from "src/utils/enum"; -import { DataSource, QueryRunner } from "typeorm"; +import { DataSource } from "typeorm"; import { TransactionalTestContext } from "typeorm-transactional-tests"; describe("PurchaseService 통합 테스트", () => { diff --git a/BE/test/int/shapes.repository.int-spec.ts b/BE/test/int/shapes.repository.int-spec.ts index 2b502f0..e207cdd 100644 --- a/BE/test/int/shapes.repository.int-spec.ts +++ b/BE/test/int/shapes.repository.int-spec.ts @@ -6,9 +6,13 @@ import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { Shape } from "src/shapes/shapes.entity"; import { ShapesModule } from "src/shapes/shapes.module"; import { ShapesRepository } from "src/shapes/shapes.repository"; +import { DataSource } from "typeorm"; +import { TransactionalTestContext } from "typeorm-transactional-tests"; describe("ShapesRepository 통합 테스트", () => { let shapesRepository: ShapesRepository; + let dataSource: DataSource; + let transactionalContext: TransactionalTestContext; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ @@ -26,8 +30,17 @@ describe("ShapesRepository 통합 테스트", () => { providers: [ShapesRepository], }).compile(); - shapesRepository = - await moduleFixture.get(ShapesRepository); + shapesRepository = moduleFixture.get(ShapesRepository); + dataSource = moduleFixture.get(DataSource); + }); + + beforeEach(async () => { + transactionalContext = new TransactionalTestContext(dataSource); + await transactionalContext.start(); + }); + + afterEach(async () => { + await transactionalContext.finish(); }); afterAll(async () => {}); @@ -53,7 +66,7 @@ describe("ShapesRepository 통합 테스트", () => { // 현재는 기본 모양 15개와 기존에 임시로 사용하던 기본 모양 3개가 모두 있음 // 추후 임시 모양 삭제 시 15로 수정할 것 - expect(result.length).toBe(18); + expect(result.length).toBe(15); }); }); diff --git a/BE/test/int/shapes.service.int-spec.ts b/BE/test/int/shapes.service.int-spec.ts index cf28289..51beec5 100644 --- a/BE/test/int/shapes.service.int-spec.ts +++ b/BE/test/int/shapes.service.int-spec.ts @@ -14,13 +14,13 @@ describe("ShapesService 통합 테스트", () => { let shapeService: ShapesService; let shapesRepository: ShapesRepository; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ providers: [ShapesService, ShapesRepository], }).compile(); - shapeService = module.get(ShapesService); - shapesRepository = module.get(ShapesRepository); + shapeService = moduleFixture.get(ShapesService); + shapesRepository = moduleFixture.get(ShapesRepository); }); afterEach(async () => { diff --git a/BE/test/int/stat.service.int-spec.ts b/BE/test/int/stat.service.int-spec.ts index fb9353d..067cbac 100644 --- a/BE/test/int/stat.service.int-spec.ts +++ b/BE/test/int/stat.service.int-spec.ts @@ -1,112 +1,104 @@ import { Test, TestingModule } from "@nestjs/testing"; import { TypeOrmModule } from "@nestjs/typeorm"; import { typeORMTestConfig } from "src/configs/typeorm.test.config"; -import { Diary } from "src/diaries/diaries.entity"; import { StatService } from "src/stat/stat.service"; +import { DataSource } from "typeorm"; +import { TransactionalTestContext } from "typeorm-transactional-tests"; describe("StatService 통합 테스트", () => { let service: StatService; + let dataSource: DataSource; + let transactionalContext: TransactionalTestContext; beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ + const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [TypeOrmModule.forRoot(typeORMTestConfig)], providers: [StatService], }).compile(); - service = module.get(StatService); + service = moduleFixture.get(StatService); + dataSource = moduleFixture.get(DataSource); + }); + + beforeEach(async () => { + transactionalContext = new TransactionalTestContext(dataSource); + await transactionalContext.start(); }); afterEach(async () => { + await transactionalContext.finish(); await jest.restoreAllMocks(); }); describe("getTopThreeTagsByUser 메서드", () => { it("메서드 정상 요청", async () => { const year = 2023; - const userId = 1; - - const mockQueryBuilder = { - select: jest.fn().mockReturnThis(), - addSelect: jest.fn().mockReturnThis(), - innerJoin: jest.fn().mockReturnThis(), - where: jest.fn().mockReturnThis(), - andWhere: jest.fn().mockReturnThis(), - groupBy: jest.fn().mockReturnThis(), - addGroupBy: jest.fn().mockReturnThis(), - orderBy: jest.fn().mockReturnThis(), - limit: jest.fn().mockReturnThis(), - getRawMany: jest.fn().mockResolvedValue([ - { tag: "태그1", id: 1, count: 10 }, - { tag: "태그2", id: 2, count: 9 }, - { tag: "태그3", id: 3, count: 8 }, - ]), - }; - - jest - .spyOn(Diary, "createQueryBuilder") - .mockReturnValue(mockQueryBuilder as any); + const userId = 2; const result = await service.getTopThreeTagsByUser(year, userId); + const expectedResult = { + first: { rank: 1, tag: "Old", id: 1, count: 15 }, + second: { rank: 2, tag: "중립", id: 8, count: 6 }, + third: { rank: 3, tag: "긍정", id: 3, count: 5 }, + }; - expect(result).toEqual({ - first: { rank: 1, tag: "태그1", id: 1, count: 10 }, - second: { rank: 2, tag: "태그2", id: 2, count: 9 }, - third: { rank: 3, tag: "태그3", id: 3, count: 8 }, - }); + expect(result).toEqual(expectedResult); }); }); describe("getDiariesDateByUser 메서드", () => { it("메서드 정상 요청", async () => { const year = 2023; - const userId = 1; + const userId = 2; const result = await service.getDiariesDateByUser(year, userId); + const expectedResult = { + "2023-01-03": { sentiment: "positive", count: 1 }, + "2023-01-06": { sentiment: "neutral", count: 1 }, + "2023-02-22": { sentiment: "neutral", count: 1 }, + "2023-03-03": { sentiment: "negative", count: 1 }, + "2023-03-17": { sentiment: "positive", count: 1 }, + "2023-05-20": { sentiment: "negative", count: 1 }, + "2023-06-06": { sentiment: "negative", count: 1 }, + "2023-08-01": { sentiment: "positive", count: 1 }, + "2023-09-04": { sentiment: "neutral", count: 1 }, + "2023-09-23": { sentiment: "positive", count: 1 }, + "2023-10-01": { sentiment: "negative", count: 1 }, + "2023-10-10": { sentiment: "negative", count: 1 }, + "2023-10-29": { sentiment: "positive", count: 1 }, + "2023-11-01": { sentiment: "neutral", count: 1 }, + "2023-12-25": { sentiment: "neutral", count: 1 }, + }; - expect(Object.keys(result).includes("2023-08-01")).toEqual(true); - expect(Object.keys(result).includes("2023-08-02")).toEqual(true); - expect(Object.keys(result).includes("2023-08-03")).toEqual(true); + expect(result).toEqual(expectedResult); }); }); describe("getTopThreeShapesByUser 메서드", () => { it("메서드 정상 요청", async () => { const year = 2023; - const userId = 1; - const mockQueryBuilder = { - select: jest.fn().mockReturnThis(), - addSelect: jest.fn().mockReturnThis(), - innerJoin: jest.fn().mockReturnThis(), - where: jest.fn().mockReturnThis(), - andWhere: jest.fn().mockReturnThis(), - groupBy: jest.fn().mockReturnThis(), - addGroupBy: jest.fn().mockReturnThis(), - orderBy: jest.fn().mockReturnThis(), - limit: jest.fn().mockReturnThis(), - getRawMany: jest.fn().mockResolvedValue([ - { uuid: "5d94024c-0f41-4e42-b766-a4f298766f67", count: 6 }, - { uuid: "5c670cee-f1e1-42ee-9b97-1ff5f561049e", count: 5 }, - ]), - }; - - jest - .spyOn(Diary, "createQueryBuilder") - .mockReturnValue(mockQueryBuilder as any); + const userId = 2; const result = await service.getTopThreeShapesByUser(year, userId); - - expect(result).toEqual({ + const expectedResult = { first: { rank: 1, - uuid: "5d94024c-0f41-4e42-b766-a4f298766f67", + uuid: "eaa0f81e-c6a8-4446-8c5a-44ea1a50bee8", count: 6, }, second: { rank: 2, - uuid: "5c670cee-f1e1-42ee-9b97-1ff5f561049e", - count: 5, + uuid: "43cb83db-8089-447c-a146-bfa07336528b", + count: 6, }, - }); + third: { + rank: 3, + uuid: "7774e2c6-f5b4-47f6-887d-63a6230b4a30", + count: 3, + }, + }; + + expect(result).toEqual(expectedResult); }); }); }); diff --git a/BE/test/int/tags.repository.int-spec.ts b/BE/test/int/tags.repository.int-spec.ts index c174b55..dc8b19f 100644 --- a/BE/test/int/tags.repository.int-spec.ts +++ b/BE/test/int/tags.repository.int-spec.ts @@ -6,9 +6,13 @@ import { TagsRepository } from "src/tags/tags.repository"; import { TagsModule } from "src/tags/tags.module"; import { Tag } from "src/tags/tags.entity"; import { NotFoundException } from "@nestjs/common"; +import { TransactionalTestContext } from "typeorm-transactional-tests"; +import { DataSource } from "typeorm"; describe("UsersRepository 통합 테스트", () => { let tagsRepository: TagsRepository; + let dataSource: DataSource; + let transactionalContext: TransactionalTestContext; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ @@ -27,6 +31,16 @@ describe("UsersRepository 통합 테스트", () => { }).compile(); tagsRepository = moduleFixture.get(TagsRepository); + dataSource = moduleFixture.get(DataSource); + }); + + beforeEach(async () => { + transactionalContext = new TransactionalTestContext(dataSource); + await transactionalContext.start(); + }); + + afterEach(async () => { + await transactionalContext.finish(); }); describe("createTags 메서드", () => { @@ -53,9 +67,6 @@ describe("UsersRepository 통합 테스트", () => { it("존재하지 않는 태그명으로 요청 시 실패", async () => { const tagName = "tagTest2"; - - const result = await tagsRepository.getTagByName(tagName); - try { await tagsRepository.getTagByName(tagName); } catch (error) { diff --git a/BE/test/int/user.repository.int-spec.ts b/BE/test/int/user.repository.int-spec.ts index fb7c4b3..8eb7702 100644 --- a/BE/test/int/user.repository.int-spec.ts +++ b/BE/test/int/user.repository.int-spec.ts @@ -7,10 +7,13 @@ import { UsersRepository } from "src/auth/users.repository"; import { CreateUserDto } from "src/auth/dto/users.dto"; import { ConflictException } from "@nestjs/common"; import { User } from "src/auth/users.entity"; -import { clearUserDb } from "src/utils/clearDb"; +import { DataSource } from "typeorm"; +import { TransactionalTestContext } from "typeorm-transactional-tests"; describe("UsersRepository 통합 테스트", () => { let usersRepository: UsersRepository; + let dataSource: DataSource; + let transactionalContext: TransactionalTestContext; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ @@ -28,9 +31,17 @@ describe("UsersRepository 통합 테스트", () => { providers: [UsersRepository], }).compile(); - usersRepository = await moduleFixture.get(UsersRepository); + usersRepository = moduleFixture.get(UsersRepository); + dataSource = moduleFixture.get(DataSource); + }); + + beforeEach(async () => { + transactionalContext = new TransactionalTestContext(dataSource); + await transactionalContext.start(); + }); - await clearUserDb(moduleFixture, usersRepository); + afterEach(async () => { + await transactionalContext.finish(); }); describe("createUser 메서드", () => { From 6049bf0c43493d67e3f51c25175702ed65c7c6a7 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Mon, 11 Dec 2023 22:59:40 +0900 Subject: [PATCH 04/18] =?UTF-8?q?Test:=20authService=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 중복된 함수 모듈화 - Redis에 저장된 refreshToken 검사 추가 및 관련 함수 구현 - accessToken 검사 --- BE/src/auth/auth.controller.ts | 2 +- BE/src/auth/auth.service.ts | 18 ++++- BE/test/int/auth.service.int-spec.ts | 116 ++++++++++++++++++++------- 3 files changed, 102 insertions(+), 34 deletions(-) diff --git a/BE/src/auth/auth.controller.ts b/BE/src/auth/auth.controller.ts index 5273780..4dae695 100644 --- a/BE/src/auth/auth.controller.ts +++ b/BE/src/auth/auth.controller.ts @@ -64,7 +64,7 @@ export class AuthController { @UseGuards(ExpiredOrNotGuard) @HttpCode(204) async signOut(@GetUser() user: User): Promise { - await this.authService.signOut(user); + await this.authService.signOut(user.userId); } @Post("/reissue") diff --git a/BE/src/auth/auth.service.ts b/BE/src/auth/auth.service.ts index 26a1dda..79f702a 100644 --- a/BE/src/auth/auth.service.ts +++ b/BE/src/auth/auth.service.ts @@ -10,6 +10,7 @@ import { Redis } from "ioredis"; import { InjectRedis } from "@liaoliaots/nestjs-redis"; import { Request } from "express"; import { providerEnum } from "src/utils/enum"; +import * as jwt from "jsonwebtoken"; @Injectable() export class AuthService { @@ -42,8 +43,8 @@ export class AuthService { return new LoginResponseDto(accessToken, nickname, premium); } - async signOut(user: User): Promise { - await this.redisClient.del(user.userId); + async signOut(userId: string): Promise { + await this.redisClient.del(userId); } async reissueAccessToken(request: Request): Promise { @@ -109,4 +110,17 @@ export class AuthService { return accessToken; } + + extractJwtToken(accessToken: string) { + const jwtPayload = jwt.verify( + accessToken, + process.env.JWT_SECRET, + ) as jwt.JwtPayload; + + return jwtPayload; + } + + async getRefreshTokenFromRedis(key: string): Promise { + return this.redisClient.get(key); + } } diff --git a/BE/test/int/auth.service.int-spec.ts b/BE/test/int/auth.service.int-spec.ts index edc96fe..b57ba5f 100644 --- a/BE/test/int/auth.service.int-spec.ts +++ b/BE/test/int/auth.service.int-spec.ts @@ -14,11 +14,14 @@ import { LoginResponseDto } from "src/auth/dto/login-response.dto"; import { NotFoundException } from "@nestjs/common"; import { DataSource } from "typeorm"; import { TransactionalTestContext } from "typeorm-transactional-tests"; +import { premiumStatus, providerEnum } from "src/utils/enum"; +import * as bcrypt from "bcryptjs"; describe("AuthService 통합 테스트", () => { let authService: AuthService; let dataSource: DataSource; let transactionalContext: TransactionalTestContext; + const ip: string = "111.111.111.111"; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ @@ -49,6 +52,27 @@ describe("AuthService 통합 테스트", () => { await jest.restoreAllMocks(); }); + async function performLoginTest( + result: LoginResponseDto, + expectedUserId: string, + expectedNickname: string, + expectedPremium: premiumStatus, + expectedRequestIp: string = "111.111.111.111", + ) { + const { userId } = authService.extractJwtToken(result.accessToken); + const refreshToken = await authService.getRefreshTokenFromRedis(userId); + const { requestIp, accessToken } = + authService.extractJwtToken(refreshToken); + + expect(result).toBeInstanceOf(LoginResponseDto); + expect(result.nickname).toBe(expectedNickname); + expect(result.premium).toBe(expectedPremium); + + expect(userId).toBe(expectedUserId); + expect(requestIp).toBe(expectedRequestIp); + expect(accessToken).toEqual(result.accessToken); + } + describe("signUp 메서드", () => { it("메서드 정상 요청", async () => { const createUserDto = new CreateUserDto(); @@ -60,23 +84,37 @@ describe("AuthService 통합 테스트", () => { const result = await authService.signUp(createUserDto); expect(result).toBeInstanceOf(User); - expect(result.userId).toBe("ValidUser123"); + expect(result).toMatchObject({ + userId: "ValidUser123", + email: "valid.email@test.com", + provider: providerEnum.BYEOLSOOP, + credit: 0, + premium: premiumStatus.FALSE, + }); + + const isPasswordMatch = await bcrypt.compare( + createUserDto.password, + result.password, + ); + expect(isPasswordMatch).toEqual(true); }); }); - describe("signIn 메서드", () => { + describe("기존 유저 signIn 메서드", () => { it("메서드 정상 요청", async () => { const authCredentialsDto: AuthCredentialsDto = { userId: "oldUser", password: "oldUser", }; - const request = { ip: "111.111.111.111" } as Request; + const request = { ip } as Request; const result = await authService.signIn(authCredentialsDto, request); - - expect(result).toBeInstanceOf(LoginResponseDto); - expect(result.nickname).toBe("기존유저"); - expect(result.premium).toBe("TRUE"); + performLoginTest( + result, + authCredentialsDto.userId, + "기존유저", + premiumStatus.TRUE, + ); }); it("존재하지 않는 아이디로 요청 시 실패", async () => { @@ -84,12 +122,13 @@ describe("AuthService 통합 테스트", () => { userId: "notFoundUser", password: "notFoundUser", }; - const request = { ip: "111.111.111.111" } as Request; + const request = { ip } as Request; try { await authService.signIn(authCredentialsDto, request); } catch (error) { expect(error).toBeInstanceOf(NotFoundException); + expect(error.message).toBe("존재하지 않는 아이디입니다."); } }); @@ -98,39 +137,51 @@ describe("AuthService 통합 테스트", () => { userId: "commonUser", password: "commonUser", }; - const request = { ip: "111.111.111.111" } as Request; + const request = { ip } as Request; try { await authService.signIn(authCredentialsDto, request); } catch (error) { expect(error).toBeInstanceOf(NotFoundException); + expect(error.message).toBe("올바르지 않은 비밀번호입니다."); } }); }); - describe("signOut 메서드", () => { + describe("기존유저 signOut 메서드", () => { it("메서드 정상 요청", async () => { const authCredentialsDto: AuthCredentialsDto = { - userId: "commonUser", - password: process.env.COMMON_USER_PASS, + userId: "oldUser", + password: "oldUser", }; const request = { ip: "111.111.111.111" } as Request; - const user = await User.findOne({ where: { userId: "commonUser" } }); - await authService.signIn(authCredentialsDto, request); - await authService.signOut(user); + { + const refreshToken = await authService.getRefreshTokenFromRedis( + authCredentialsDto.userId, + ); + expect(refreshToken).not.toBeNull(); + } + + await authService.signOut(authCredentialsDto.userId); + { + const refreshToken = await authService.getRefreshTokenFromRedis( + authCredentialsDto.userId, + ); + expect(refreshToken).toBeNull(); + } }); }); - describe("reissueAccessToken 메서드", () => { + describe("기존유저 reissueAccessToken 메서드", () => { it("메서드 정상 요청", async () => { const authCredentialsDto: AuthCredentialsDto = { - userId: "commonUser", - password: process.env.COMMON_USER_PASS, + userId: "oldUser", + password: "oldUser", }; const request = { - ip: "111.111.111.111", + ip, headers: { authorization: "" }, } as Request; @@ -140,33 +191,36 @@ describe("AuthService 통합 테스트", () => { ); request.headers.authorization = `Bearer ${accessToken}`; - const result = await authService.reissueAccessToken(request); - expect(result).toBeInstanceOf(LoginResponseDto); + const result = await authService.reissueAccessToken(request); + performLoginTest( + result, + authCredentialsDto.userId, + "기존유저", + premiumStatus.TRUE, + ); }); }); describe("naverSignIn 메서드", () => { it("메서드 정상 요청", async () => { - const user = await User.findOne({ where: { userId: "naverUser" } }); - const request = { ip: "111.111.111.111" } as Request; + const userId = "naverUser"; + const user = await User.findOne({ where: { userId } }); + const request = { ip } as Request; const result = await authService.naverSignIn(user, request); - expect(result).toBeInstanceOf(LoginResponseDto); - expect(result.nickname).toBe("네이버유저"); - expect(result.premium).toBe("FALSE"); + performLoginTest(result, userId, "네이버유저", premiumStatus.FALSE); }); }); describe("kakaoSignIn 메서드", () => { it("메서드 정상 요청", async () => { - const user = await User.findOne({ where: { userId: "kakaoUser" } }); - const request = { ip: "111.111.111.111" } as Request; + const userId = "kakaoUser"; + const user = await User.findOne({ where: { userId } }); + const request = { ip } as Request; const result = await authService.naverSignIn(user, request); - expect(result).toBeInstanceOf(LoginResponseDto); - expect(result.nickname).toBe("카카오유저"); - expect(result.premium).toBe("TRUE"); + performLoginTest(result, userId, "카카오유저", premiumStatus.TRUE); }); }); }); From 883d92f4c6c27f189d39cadcda132fdf08654761 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Mon, 11 Dec 2023 23:11:18 +0900 Subject: [PATCH 05/18] =?UTF-8?q?test:=20user=20repository=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 중복된 이메일 테스트 코드 제거 - 존재하지 않는 유저 getUserByUserId로 조회 - commonUser, 네이버 유저, 카카오 유저에 대한 getUserByUserId 검사 --- BE/test/int/user.repository.int-spec.ts | 72 ++++++++++++++++++------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/BE/test/int/user.repository.int-spec.ts b/BE/test/int/user.repository.int-spec.ts index 8eb7702..6a514b7 100644 --- a/BE/test/int/user.repository.int-spec.ts +++ b/BE/test/int/user.repository.int-spec.ts @@ -9,6 +9,7 @@ import { ConflictException } from "@nestjs/common"; import { User } from "src/auth/users.entity"; import { DataSource } from "typeorm"; import { TransactionalTestContext } from "typeorm-transactional-tests"; +import { premiumStatus, providerEnum } from "src/utils/enum"; describe("UsersRepository 통합 테스트", () => { let usersRepository: UsersRepository; @@ -53,9 +54,14 @@ describe("UsersRepository 통합 테스트", () => { createUserDto.nickname = "ValidNickname"; const result = await usersRepository.createUser(createUserDto); - expect(result).toBeInstanceOf(User); - expect(result.userId).toBe("ValidUser123"); + expect(result).toMatchObject({ + userId: "ValidUser123", + email: "valid.email@test.com", + provider: providerEnum.BYEOLSOOP, + credit: 0, + premium: premiumStatus.FALSE, + }); }); it("중복된 아이디로 요청 시 실패", async () => { @@ -69,32 +75,62 @@ describe("UsersRepository 통합 테스트", () => { await usersRepository.createUser(createUserDto); } catch (error) { expect(error).toBeInstanceOf(ConflictException); - } - }); - - it("중복된 이메일로 요청 시 실패", async () => { - const createUserDto = new CreateUserDto(); - createUserDto.userId = "ValidUser1234"; - createUserDto.email = "valid.email@test.com"; - createUserDto.password = "ValidPass123!"; - createUserDto.nickname = "ValidNickname"; - - try { - await usersRepository.createUser(createUserDto); - } catch (error) { - expect(error).toBeInstanceOf(ConflictException); + expect(error.message).toBe("중복된 아이디입니다."); } }); }); describe("getUserByUserId 메서드", () => { - it("메서드 정상 요청", async () => { + it("commonUser 메서드 정상 요청", async () => { const userId = "commonUser"; const result = await usersRepository.getUserByUserId(userId); expect(result).toBeInstanceOf(User); - expect(result.email).toBe("byeolsoop08@naver.com"); + expect(result).toMatchObject({ + userId: "commonUser", + email: "byeolsoop08@naver.com", + provider: providerEnum.BYEOLSOOP, + credit: 0, + premium: premiumStatus.FALSE, + }); + }); + + it("네이버유저 메서드 정상 요청", async () => { + const userId = "naverUser"; + + const result = await usersRepository.getUserByUserId(userId); + + expect(result).toBeInstanceOf(User); + expect(result).toMatchObject({ + userId: "naverUser", + email: "naverUser@naver.com", + provider: providerEnum.NAVER, + credit: 500, + premium: premiumStatus.FALSE, + }); + }); + + it("카카오유저 메서드 정상 요청", async () => { + const userId = "kakaoUser"; + + const result = await usersRepository.getUserByUserId(userId); + + expect(result).toBeInstanceOf(User); + expect(result).toMatchObject({ + userId: "kakaoUser", + email: "kakaoUser@kakao.com", + provider: providerEnum.KAKAO, + credit: 1000, + premium: premiumStatus.TRUE, + }); + }); + + it("존재하지 않는 사용자 id 조회", async () => { + const userId = "notFoundUser"; + + const result = await usersRepository.getUserByUserId(userId); + expect(result).toBeNull(); }); }); }); From aa0df203de7b077b90870e3b820f67f7243a9585 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Mon, 11 Dec 2023 23:18:40 +0900 Subject: [PATCH 06/18] =?UTF-8?q?test:=20tags=20repository=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DB에 없는 태그로 생성 - 불필요한 await 제거 --- BE/src/tags/tags.repository.ts | 2 +- BE/test/int/tags.repository.int-spec.ts | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/BE/src/tags/tags.repository.ts b/BE/src/tags/tags.repository.ts index 1f84a51..92a5ec1 100644 --- a/BE/src/tags/tags.repository.ts +++ b/BE/src/tags/tags.repository.ts @@ -3,7 +3,7 @@ import { NotFoundException } from "@nestjs/common"; export class TagsRepository { async createTag(name: string): Promise { - const tag = await Tag.create({ name }); + const tag = Tag.create({ name }); await tag.save(); return tag; diff --git a/BE/test/int/tags.repository.int-spec.ts b/BE/test/int/tags.repository.int-spec.ts index dc8b19f..58917a1 100644 --- a/BE/test/int/tags.repository.int-spec.ts +++ b/BE/test/int/tags.repository.int-spec.ts @@ -9,7 +9,7 @@ import { NotFoundException } from "@nestjs/common"; import { TransactionalTestContext } from "typeorm-transactional-tests"; import { DataSource } from "typeorm"; -describe("UsersRepository 통합 테스트", () => { +describe("TagsRepository 통합 테스트", () => { let tagsRepository: TagsRepository; let dataSource: DataSource; let transactionalContext: TransactionalTestContext; @@ -45,32 +45,33 @@ describe("UsersRepository 통합 테스트", () => { describe("createTags 메서드", () => { it("메서드 정상 요청", async () => { - const tagName = "tagTest"; + const tagName = "getTagByNameTagTest"; const result = await tagsRepository.createTag(tagName); expect(result).toBeInstanceOf(Tag); - expect(result.name).toBe("tagTest"); + expect(result.name).toBe("getTagByNameTagTest"); }); }); describe("getTagByName 메서드", () => { it("메서드 정상 요청", async () => { - const tagName = "tagTest"; + const tagName = "getTagByNameTagTest"; await tagsRepository.createTag(tagName); const result = await tagsRepository.getTagByName(tagName); expect(result).toBeInstanceOf(Tag); - expect(result.name).toBe("tagTest"); + expect(result.name).toEqual(tagName); }); it("존재하지 않는 태그명으로 요청 시 실패", async () => { - const tagName = "tagTest2"; + const tagName = "NoTagTest"; try { await tagsRepository.getTagByName(tagName); } catch (error) { expect(error).toBeInstanceOf(NotFoundException); + expect(error.message).toBe(`Can't find Tag with name: [${tagName}]`); } }); }); From cd1ab4dc40ed498455fcebdcce7d04db66d913dc Mon Sep 17 00:00:00 2001 From: jeongmin Date: Mon, 11 Dec 2023 23:32:53 +0900 Subject: [PATCH 07/18] =?UTF-8?q?test:=20stat=20service=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 네이버 / 카카오 사용자 검사 추가 --- BE/test/int/stat.service.int-spec.ts | 77 ++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/BE/test/int/stat.service.int-spec.ts b/BE/test/int/stat.service.int-spec.ts index 067cbac..ece24ad 100644 --- a/BE/test/int/stat.service.int-spec.ts +++ b/BE/test/int/stat.service.int-spec.ts @@ -31,7 +31,7 @@ describe("StatService 통합 테스트", () => { }); describe("getTopThreeTagsByUser 메서드", () => { - it("메서드 정상 요청", async () => { + it("기존유저 메서드 정상 요청", async () => { const year = 2023; const userId = 2; @@ -44,10 +44,34 @@ describe("StatService 통합 테스트", () => { expect(result).toEqual(expectedResult); }); + + it("신규유저 메서드 정상 요청", async () => { + const year = 2023; + const userId = 3; + + const result = await service.getTopThreeTagsByUser(year, userId); + const expectedResult = {}; + + expect(result).toEqual(expectedResult); + }); + + it("네이버유저 메서드 정상 요청", async () => { + const year = 2023; + const userId = 4; + + const result = await service.getTopThreeTagsByUser(year, userId); + const expectedResult = { + first: { rank: 1, tag: "긍정", id: 3, count: 3 }, + second: { rank: 2, tag: "Naver", id: 13, count: 3 }, + third: { rank: 3, tag: "3월", id: 6, count: 1 }, + }; + + expect(result).toEqual(expectedResult); + }); }); describe("getDiariesDateByUser 메서드", () => { - it("메서드 정상 요청", async () => { + it("기존유저 메서드 정상 요청", async () => { const year = 2023; const userId = 2; @@ -72,10 +96,32 @@ describe("StatService 통합 테스트", () => { expect(result).toEqual(expectedResult); }); + + it("신규유저 메서드 정상 요청", async () => { + const year = 2023; + const userId = 3; + + const result = await service.getDiariesDateByUser(year, userId); + const expectedResult = {}; + + expect(result).toEqual(expectedResult); + }); + + it("카카오유저 메서드 정상 요청", async () => { + const year = 2023; + const userId = 5; + + const result = await service.getDiariesDateByUser(year, userId); + const expectedResult = { + "2023-06-13": { sentiment: "neutral", count: 2 }, + }; + + expect(result).toEqual(expectedResult); + }); }); describe("getTopThreeShapesByUser 메서드", () => { - it("메서드 정상 요청", async () => { + it("기존유저 메서드 정상 요청", async () => { const year = 2023; const userId = 2; @@ -100,5 +146,30 @@ describe("StatService 통합 테스트", () => { expect(result).toEqual(expectedResult); }); + + it("신규유저 메서드 정상 요청", async () => { + const year = 2023; + const userId = 3; + + const result = await service.getTopThreeShapesByUser(year, userId); + const expectedResult = {}; + + expect(result).toEqual(expectedResult); + }); + + it("네이버유저 메서드 정상 요청", async () => { + const year = 2023; + const userId = 4; + + const result = await service.getTopThreeShapesByUser(year, userId); + const expectedResult = { + first: { + rank: 1, + uuid: "43cb83db-8089-447c-a146-bfa07336528b", + count: 3, + }, + }; + expect(result).toEqual(expectedResult); + }); }); }); From 497fbd6c3c686c78de9e32f3f2312880e894e7e6 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Tue, 12 Dec 2023 01:10:57 +0900 Subject: [PATCH 08/18] =?UTF-8?q?test:=20shapesService=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DB에 저장된 기본 모양 데이터 활용 --- BE/test/int/shapes.service.int-spec.ts | 62 +++++++++++++------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/BE/test/int/shapes.service.int-spec.ts b/BE/test/int/shapes.service.int-spec.ts index 51beec5..452e7de 100644 --- a/BE/test/int/shapes.service.int-spec.ts +++ b/BE/test/int/shapes.service.int-spec.ts @@ -4,6 +4,10 @@ import { ShapesService } from "src/shapes/shapes.service"; import { ShapesRepository } from "src/shapes/shapes.repository"; import { Shape } from "src/shapes/shapes.entity"; import { User } from "src/auth/users.entity"; +import { DataSource } from "typeorm"; +import { TransactionalTestContext } from "typeorm-transactional-tests"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { defaultShapes } from "src/shapes/shapes.default"; jest.mock("src/utils/s3", () => ({ @@ -13,45 +17,52 @@ jest.mock("src/utils/s3", () => ({ describe("ShapesService 통합 테스트", () => { let shapeService: ShapesService; let shapesRepository: ShapesRepository; + let dataSource: DataSource; + let transactionalContext: TransactionalTestContext; + let oldUser: User; + const shapeUuid = "8d010933-03f6-48f1-aa9f-5e4771eaf28c"; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [TypeOrmModule.forRoot(typeORMTestConfig)], providers: [ShapesService, ShapesRepository], }).compile(); shapeService = moduleFixture.get(ShapesService); shapesRepository = moduleFixture.get(ShapesRepository); + dataSource = moduleFixture.get(DataSource); + + oldUser = await User.findOne({ where: { userId: "oldUser" } }); + }); + + beforeEach(async () => { + transactionalContext = new TransactionalTestContext(dataSource); + await transactionalContext.start(); }); afterEach(async () => { - await jest.clearAllMocks(); + await transactionalContext.finish(); + await jest.restoreAllMocks(); }); describe("getDefaultShapeFiles 메서드", () => { it("메서드 정상 요청", async () => { - const shape: Shape = new Shape(); - jest - .spyOn(shapesRepository, "getShapeByShapePath") - .mockResolvedValue(shape); - const shapes = await shapeService.getDefaultShapeFiles(); expect(shapes).toHaveLength(defaultShapes.length); expect(shapes.every((shape) => shape instanceof Shape)).toBe(true); + expect(shapes.map((shape) => shape.shapePath).sort()).toEqual( + defaultShapes.map((shape) => shape.shapePath).sort(), + ); }); }); describe("getShapeFileByUuid 메서드", () => { it("메서드 정상 요청", async () => { - const shape: Shape = new Shape(); - shape.user = new User(); - jest.spyOn(shapesRepository, "getShapeByUuid").mockResolvedValue(shape); - const shapeFile = await shapeService.getShapeFileByUuid( - "uuid", - new User(), + shapeUuid, + oldUser, ); - expect(shapeFile).toBe("shape_svg_string"); }); @@ -63,32 +74,19 @@ describe("ShapesService 통합 테스트", () => { jest.spyOn(shapesRepository, "getShapeByUuid").mockResolvedValue(shape); await expect( - shapeService.getShapeFileByUuid("uuid", new User()), + shapeService.getShapeFileByUuid("uuid", oldUser), ).rejects.toThrow(UnauthorizedException); }); }); describe("getShapesByUser 메서드", () => { it("메서드 정상 요청", async () => { - const shapeDefault: Shape = new Shape(); - const shapeUser: Shape = new Shape(); - - jest - .spyOn(shapeService, "getDefaultShapeFiles") - // 원래는 15개의 기본 모양이 있지만, mocking 용으로 3개의 모양만 반환하도록 함 - .mockResolvedValue([shapeDefault, shapeDefault, shapeDefault]); - jest - .spyOn(shapesRepository, "getShapesByUser") - .mockResolvedValue([shapeUser]); - jest - .spyOn(shapeService, "getShapeFileByUuid") - .mockResolvedValue("shape_svg_string"); + const [shapeUuidList, shapeFileList] = + await shapeService.getShapesByUser(oldUser); - const [shapeUuidList, shapeFileList] = await shapeService.getShapesByUser( - new User(), - ); - expect(shapeUuidList).toHaveLength(4); - expect(shapeFileList).toHaveLength(4); + expect(shapeUuidList).toHaveLength(15); + expect(shapeUuidList).toContain(shapeUuid); + expect(shapeFileList).toHaveLength(15); expect(shapeFileList.every((file) => file === "shape_svg_string")).toBe( true, ); From 4e1127f8bdb49f3d0e0c004a4ebd7a4467ed1eab Mon Sep 17 00:00:00 2001 From: jeongmin Date: Tue, 12 Dec 2023 01:56:06 +0900 Subject: [PATCH 09/18] =?UTF-8?q?refactor:=20getShapesByUser()=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EC=97=90=20userId=EB=A5=BC=20=EC=9D=B8=EC=9E=90?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/shapes/shapes.repository.ts | 11 +++-------- BE/src/shapes/shapes.service.ts | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/BE/src/shapes/shapes.repository.ts b/BE/src/shapes/shapes.repository.ts index 1b1dd94..5021515 100644 --- a/BE/src/shapes/shapes.repository.ts +++ b/BE/src/shapes/shapes.repository.ts @@ -32,14 +32,9 @@ export class ShapesRepository { return found; } - async getShapesByUser(user: User): Promise { - const shapeList: Shape[] = await Shape.find({ - where: { user: { userId: user.userId } }, + async getShapesByUser(userId: string): Promise { + return Shape.find({ + where: { user: { userId } }, }); - if (!shapeList) { - throw new NotFoundException("존재하지 않는 사용자입니다."); - } - - return shapeList; } } diff --git a/BE/src/shapes/shapes.service.ts b/BE/src/shapes/shapes.service.ts index 63a32ef..4840907 100644 --- a/BE/src/shapes/shapes.service.ts +++ b/BE/src/shapes/shapes.service.ts @@ -35,7 +35,7 @@ export class ShapesService { if (user.userId !== "commonUser") { shapeList = defaultShapeList.concat( - await this.shapesRepository.getShapesByUser(user), + await this.shapesRepository.getShapesByUser(user.userId), ); } else { shapeList = defaultShapeList; From 971df0af6323cba11388c7d40d6f4233c3522547 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Tue, 12 Dec 2023 01:56:48 +0900 Subject: [PATCH 10/18] =?UTF-8?q?test:=20ShapesRepository=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DB에 저장된 정보 사용 - 제대로 돌아가지 않던 에러 확인 테스트 삭제 --- BE/test/int/shapes.repository.int-spec.ts | 69 ++++++++--------------- 1 file changed, 23 insertions(+), 46 deletions(-) diff --git a/BE/test/int/shapes.repository.int-spec.ts b/BE/test/int/shapes.repository.int-spec.ts index e207cdd..56b84f0 100644 --- a/BE/test/int/shapes.repository.int-spec.ts +++ b/BE/test/int/shapes.repository.int-spec.ts @@ -1,10 +1,9 @@ -import { RedisModule } from "@liaoliaots/nestjs-redis"; +import { NotFoundException } from "@nestjs/common"; import { Test, TestingModule } from "@nestjs/testing"; import { TypeOrmModule } from "@nestjs/typeorm"; import { User } from "src/auth/users.entity"; import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { Shape } from "src/shapes/shapes.entity"; -import { ShapesModule } from "src/shapes/shapes.module"; import { ShapesRepository } from "src/shapes/shapes.repository"; import { DataSource } from "typeorm"; import { TransactionalTestContext } from "typeorm-transactional-tests"; @@ -13,25 +12,19 @@ describe("ShapesRepository 통합 테스트", () => { let shapesRepository: ShapesRepository; let dataSource: DataSource; let transactionalContext: TransactionalTestContext; + let commonUser: User; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [ - TypeOrmModule.forRoot(typeORMTestConfig), - ShapesModule, - RedisModule.forRoot({ - readyLog: true, - config: { - host: "223.130.129.145", - port: 6379, - }, - }), - ], + imports: [TypeOrmModule.forRoot(typeORMTestConfig)], providers: [ShapesRepository], }).compile(); shapesRepository = moduleFixture.get(ShapesRepository); dataSource = moduleFixture.get(DataSource); + commonUser = await User.findOne({ + where: { userId: "commonUser" }, + }); }); beforeEach(async () => { @@ -47,60 +40,44 @@ describe("ShapesRepository 통합 테스트", () => { describe("createDefaultShapes 통합 테스트", () => { it("메서드 정상 요청", async () => { - const commonUser = await User.findOne({ - where: { userId: "commonUser" }, - }); + await dataSource.query("SET FOREIGN_KEY_CHECKS = 0"); + await Shape.delete({}); + await dataSource.query("SET FOREIGN_KEY_CHECKS = 1"); + expect(await Shape.find()).toHaveLength(0); await shapesRepository.createDefaultShapes(commonUser); + expect(await Shape.find()).toHaveLength(15); }); }); describe("getShapesByUser 통합 테스트", () => { it("메서드 정상 요청", async () => { - const commonUser = await User.findOne({ - where: { userId: "commonUser" }, - }); - const result: Shape[] = - await shapesRepository.getShapesByUser(commonUser); + await shapesRepository.getShapesByUser("commonUser"); - // 현재는 기본 모양 15개와 기존에 임시로 사용하던 기본 모양 3개가 모두 있음 - // 추후 임시 모양 삭제 시 15로 수정할 것 - expect(result.length).toBe(15); + expect(result).toHaveLength(15); }); }); describe("getShapeByShapePath 통합 테스트", () => { it("메서드 정상 요청", async () => { - const shape = await Shape.findOne({ - where: { - user: { - userId: "commonUser", - }, - }, - }); + const shapePath = "BasicShape10.svg"; + const result = await shapesRepository.getShapeByShapePath(shapePath); - const result = await shapesRepository.getShapeByShapePath( - shape.shapePath, - ); - - expect(result).toStrictEqual(shape); + expect(result.shapePath).toEqual(shapePath); + expect(result.uuid).toBe("c048ba4f-4ea9-4605-bcc9-e45103d68a4f"); + expect(result.user.userId).toBe("commonUser"); }); }); describe("getShapeByUuid 통합 테스트", () => { it("메서드 정상 요청", async () => { - const shape = await Shape.findOne({ - where: { - user: { - userId: "commonUser", - }, - }, - }); - - const result = await shapesRepository.getShapeByUuid(shape.uuid); + const uuid = "c048ba4f-4ea9-4605-bcc9-e45103d68a4f"; + const result = await shapesRepository.getShapeByUuid(uuid); - expect(result).toStrictEqual(shape); + expect(result.uuid).toEqual(uuid); + expect(result.shapePath).toBe("BasicShape10.svg"); + expect(result.user.userId).toBe("commonUser"); }); }); }); From 8d8aa1dc894a03406f5149f0f28c61d631f52150 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Tue, 12 Dec 2023 02:19:12 +0900 Subject: [PATCH 11/18] =?UTF-8?q?test:=20request=EC=97=90=20headers=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/test/int/auth.service.int-spec.ts | 35 +++++++--------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/BE/test/int/auth.service.int-spec.ts b/BE/test/int/auth.service.int-spec.ts index b57ba5f..f871f86 100644 --- a/BE/test/int/auth.service.int-spec.ts +++ b/BE/test/int/auth.service.int-spec.ts @@ -21,7 +21,10 @@ describe("AuthService 통합 테스트", () => { let authService: AuthService; let dataSource: DataSource; let transactionalContext: TransactionalTestContext; - const ip: string = "111.111.111.111"; + const request = { + ip: "111.111.111.111", + headers: { authorization: "" }, + } as Request; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ @@ -56,7 +59,6 @@ describe("AuthService 통합 테스트", () => { result: LoginResponseDto, expectedUserId: string, expectedNickname: string, - expectedPremium: premiumStatus, expectedRequestIp: string = "111.111.111.111", ) { const { userId } = authService.extractJwtToken(result.accessToken); @@ -66,7 +68,6 @@ describe("AuthService 통합 테스트", () => { expect(result).toBeInstanceOf(LoginResponseDto); expect(result.nickname).toBe(expectedNickname); - expect(result.premium).toBe(expectedPremium); expect(userId).toBe(expectedUserId); expect(requestIp).toBe(expectedRequestIp); @@ -106,15 +107,9 @@ describe("AuthService 통합 테스트", () => { userId: "oldUser", password: "oldUser", }; - const request = { ip } as Request; const result = await authService.signIn(authCredentialsDto, request); - performLoginTest( - result, - authCredentialsDto.userId, - "기존유저", - premiumStatus.TRUE, - ); + performLoginTest(result, authCredentialsDto.userId, "기존유저"); }); it("존재하지 않는 아이디로 요청 시 실패", async () => { @@ -122,7 +117,6 @@ describe("AuthService 통합 테스트", () => { userId: "notFoundUser", password: "notFoundUser", }; - const request = { ip } as Request; try { await authService.signIn(authCredentialsDto, request); @@ -137,7 +131,6 @@ describe("AuthService 통합 테스트", () => { userId: "commonUser", password: "commonUser", }; - const request = { ip } as Request; try { await authService.signIn(authCredentialsDto, request); @@ -154,7 +147,6 @@ describe("AuthService 통합 테스트", () => { userId: "oldUser", password: "oldUser", }; - const request = { ip: "111.111.111.111" } as Request; await authService.signIn(authCredentialsDto, request); { @@ -180,10 +172,6 @@ describe("AuthService 통합 테스트", () => { userId: "oldUser", password: "oldUser", }; - const request = { - ip, - headers: { authorization: "" }, - } as Request; const { accessToken } = await authService.signIn( authCredentialsDto, @@ -193,12 +181,7 @@ describe("AuthService 통합 테스트", () => { request.headers.authorization = `Bearer ${accessToken}`; const result = await authService.reissueAccessToken(request); - performLoginTest( - result, - authCredentialsDto.userId, - "기존유저", - premiumStatus.TRUE, - ); + performLoginTest(result, authCredentialsDto.userId, "기존유저"); }); }); @@ -206,10 +189,9 @@ describe("AuthService 통합 테스트", () => { it("메서드 정상 요청", async () => { const userId = "naverUser"; const user = await User.findOne({ where: { userId } }); - const request = { ip } as Request; const result = await authService.naverSignIn(user, request); - performLoginTest(result, userId, "네이버유저", premiumStatus.FALSE); + performLoginTest(result, userId, "네이버유저"); }); }); @@ -217,10 +199,9 @@ describe("AuthService 통합 테스트", () => { it("메서드 정상 요청", async () => { const userId = "kakaoUser"; const user = await User.findOne({ where: { userId } }); - const request = { ip } as Request; const result = await authService.naverSignIn(user, request); - performLoginTest(result, userId, "카카오유저", premiumStatus.TRUE); + performLoginTest(result, userId, "카카오유저"); }); }); }); From 11b8843082c1467a483977f7d98be3e3d042accd Mon Sep 17 00:00:00 2001 From: jeongmin Date: Wed, 13 Dec 2023 02:17:43 +0900 Subject: [PATCH 12/18] =?UTF-8?q?refactor:=20sentimentDto=EC=97=90=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/diaries/dto/diaries.sentiment.dto.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/BE/src/diaries/dto/diaries.sentiment.dto.ts b/BE/src/diaries/dto/diaries.sentiment.dto.ts index 37aceeb..46eb10c 100644 --- a/BE/src/diaries/dto/diaries.sentiment.dto.ts +++ b/BE/src/diaries/dto/diaries.sentiment.dto.ts @@ -5,4 +5,16 @@ export class SentimentDto { neutralRatio: number; negativeRatio: number; sentiment: sentimentStatus; + + constructor( + positiveRatio: number, + neutralRatio: number, + negativeRatio: number, + sentiment: sentimentStatus, + ) { + this.positiveRatio = positiveRatio; + this.neutralRatio = neutralRatio; + this.negativeRatio = negativeRatio; + this.sentiment = sentiment; + } } From eed6b9475f8040e779cff94a2afc2d3387d287f0 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Wed, 13 Dec 2023 02:18:14 +0900 Subject: [PATCH 13/18] =?UTF-8?q?test:=20DiariesRepository=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 테스트 DB 더미 데이터 반영 --- BE/test/int/diaries.repository.int-spec.ts | 172 +++++++++++++-------- 1 file changed, 107 insertions(+), 65 deletions(-) diff --git a/BE/test/int/diaries.repository.int-spec.ts b/BE/test/int/diaries.repository.int-spec.ts index 05c5d83..91226d7 100644 --- a/BE/test/int/diaries.repository.int-spec.ts +++ b/BE/test/int/diaries.repository.int-spec.ts @@ -1,10 +1,8 @@ -import { RedisModule } from "@liaoliaots/nestjs-redis"; import { Test, TestingModule } from "@nestjs/testing"; import { TypeOrmModule } from "@nestjs/typeorm"; import { User } from "src/auth/users.entity"; import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { Diary } from "src/diaries/diaries.entity"; -import { DiariesModule } from "src/diaries/diaries.module"; import { DiariesRepository } from "src/diaries/diaries.repository"; import { CreateDiaryDto, UpdateDiaryDto } from "src/diaries/dto/diaries.dto"; import { ReadDiaryDto } from "src/diaries/dto/diaries.read.dto"; @@ -18,24 +16,13 @@ import { TransactionalTestContext } from "typeorm-transactional-tests"; describe("DiariesRepository 통합 테스트", () => { let diariesRepository: DiariesRepository; - let user: User; let createdDiary: Diary; let dataSource: DataSource; let transactionalContext: TransactionalTestContext; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [ - TypeOrmModule.forRoot(typeORMTestConfig), - DiariesModule, - RedisModule.forRoot({ - readyLog: true, - config: { - host: "223.130.129.145", - port: 6379, - }, - }), - ], + imports: [TypeOrmModule.forRoot(typeORMTestConfig)], providers: [DiariesRepository], }).compile(); @@ -46,35 +33,6 @@ describe("DiariesRepository 통합 테스트", () => { beforeEach(async () => { transactionalContext = new TransactionalTestContext(dataSource); await transactionalContext.start(); - - user = await User.findOne({ where: { userId: "commonUser" } }); - const shape = await Shape.findOne({ - where: { user: { userId: "commonUser" } }, - }); - const tags = [new Tag(), new Tag()]; - - const createDiaryDto = new CreateDiaryDto(); - createDiaryDto.title = "Test Title"; - createDiaryDto.content = "Test Content"; - createDiaryDto.point = "1,1,1"; - (createDiaryDto.date as any) = "2023-11-29"; - createDiaryDto.tags = ["tag1", "tag2"]; - createDiaryDto.shapeUuid = shape.uuid; - - const sentimentDto = new SentimentDto(); - sentimentDto.positiveRatio = 0; - sentimentDto.negativeRatio = 0; - sentimentDto.neutralRatio = 100; - sentimentDto.sentiment = sentimentStatus.NEUTRAL; - - createdDiary = await diariesRepository.createDiary( - createDiaryDto, - createDiaryDto.content, - tags, - user, - shape, - sentimentDto, - ); }); afterEach(async () => { @@ -83,73 +41,157 @@ describe("DiariesRepository 통합 테스트", () => { describe("createDiary 통합 테스트", () => { it("메서드 정상 요청", async () => { + const newUser = await User.findOne({ where: { userId: "newUser" } }); + const shape = await Shape.findOne({ where: { id: 2 } }); + const tags = [await Tag.findOne({ where: { name: "Naver" } })]; + + const createDiaryDto = new CreateDiaryDto(); + createDiaryDto.title = "Test Title"; + createDiaryDto.content = "Test Content"; + createDiaryDto.point = "1,1,1"; + createDiaryDto.date = new Date("2023-11-29"); + createDiaryDto.tags = ["Naver"]; + createDiaryDto.shapeUuid = shape.uuid; + + const sentimentDto = new SentimentDto(0, 100, 0, sentimentStatus.NEUTRAL); + + createdDiary = await diariesRepository.createDiary( + createDiaryDto, + createDiaryDto.content, + tags, + newUser, + shape, + sentimentDto, + ); + expect(createdDiary).toBeInstanceOf(Diary); - expect(createdDiary.title).toBe("Test Title"); + expect(createdDiary).toMatchObject({ + title: "Test Title", + positiveRatio: 0, + negativeRatio: 0, + neutralRatio: 100, + sentiment: "neutral", + date: new Date("2023-11-29"), + tags: [{ id: 13, name: "Naver" }], + }); + expect(createdDiary.shape.id).toBe(2); + expect(createdDiary.user.id).toBe(3); }); }); describe("readDiary 통합 테스트", () => { it("메서드 정상 요청", async () => { const readDiaryDto = new ReadDiaryDto(); - readDiaryDto.uuid = createdDiary.uuid; + readDiaryDto.uuid = "0c388311-61c4-41ad-8d4d-b7b1ce075ef2"; const result = await diariesRepository.readDiary(readDiaryDto); expect(result).toBeInstanceOf(Diary); - expect(result.title).toBe("Test Title"); + expect(result).toMatchObject({ + title: "부정 1", + uuid: "0c388311-61c4-41ad-8d4d-b7b1ce075ef2", + positiveRatio: 0.00949954, + negativeRatio: 99.978, + neutralRatio: 0.0124967, + sentiment: "negative", + date: new Date("2023-03-02T15:00:00.000Z"), + tags: [ + { id: 1, name: "Old" }, + { id: 5, name: "9월" }, + { id: 8, name: "중립" }, + ], + }); }); }); describe("readDiariesByUser 통합 테스트", () => { it("메서드 정상 요청", async () => { - const result = await diariesRepository.readDiariesByUser(user); - - expect(result[0]).toBeInstanceOf(Diary); + const naverUser = await User.findOne({ where: { userId: "naverUser" } }); + const diaries = await diariesRepository.readDiariesByUser(naverUser); + + expect(diaries[0]).toBeInstanceOf(Diary); + expect(diaries).toHaveLength(3); + + const expectedTitles = [ + "긍정 네이버 1", + "긍정 네이버 2", + "긍정 네이버 3", + ]; + expect(diaries.map((diary) => diary.title)).toEqual( + expect.arrayContaining(expectedTitles), + ); }); }); describe("updateDiary 통합 테스트", () => { it("메서드 정상 요청", async () => { - const shape = await Shape.findOne({ - where: { user: { userId: "commonUser" } }, - }); - const tags = [new Tag(), new Tag()]; + const oldUser = await User.findOne({ where: { userId: "oldUser" } }); + const shape = await Shape.findOne({ where: { id: 3 } }); + const tags = [await Tag.findOne({ where: { name: "Naver" } })]; const updateDiaryDto = new UpdateDiaryDto(); updateDiaryDto.title = "Test Title"; updateDiaryDto.content = "Updated Content"; updateDiaryDto.point = "1,1,1"; - (updateDiaryDto.date as any) = "2023-11-29"; - updateDiaryDto.tags = ["tag1", "tag2"]; + updateDiaryDto.date = new Date("2023-11-29"); + updateDiaryDto.tags = ["Naver"]; updateDiaryDto.shapeUuid = shape.uuid; - updateDiaryDto.uuid = createdDiary.uuid; + updateDiaryDto.uuid = "0c388311-61c4-41ad-8d4d-b7b1ce075ef2"; - const sentimentDto = new SentimentDto(); - sentimentDto.positiveRatio = 0; - sentimentDto.negativeRatio = 0; - sentimentDto.neutralRatio = 100; - sentimentDto.sentiment = sentimentStatus.NEUTRAL; + const sentimentDto = new SentimentDto( + 80, + 20, + 0, + sentimentStatus.POSITIVE, + ); const result = await diariesRepository.updateDiary( updateDiaryDto, updateDiaryDto.content, tags, - user, + oldUser, shape, sentimentDto, ); expect(result).toBeInstanceOf(Diary); - expect(result.content).toBe("Updated Content"); + expect(result).toMatchObject({ + title: "Test Title", + content: "Updated Content", + uuid: "0c388311-61c4-41ad-8d4d-b7b1ce075ef2", + positiveRatio: 80, + neutralRatio: 20, + negativeRatio: 0, + sentiment: "positive", + date: new Date("2023-11-29"), + tags: [{ id: 13, name: "Naver" }], + }); }); }); describe("getDiaryByUuid 통합 테스트", () => { it("메서드 정상 요청", async () => { - const result = await diariesRepository.getDiaryByUuid(createdDiary.uuid); + const uuid = "c8cd1721-d553-430d-b3cd-7246967445d4"; + const result = await diariesRepository.getDiaryByUuid(uuid); expect(result).toBeInstanceOf(Diary); - expect(result.title).toBe("Test Title"); + expect(result).toMatchObject({ + id: 5, + uuid: "c8cd1721-d553-430d-b3cd-7246967445d4", + title: "긍정 5", + positiveRatio: 99.9786, + negativeRatio: 0.0180166, + neutralRatio: 0.00338173, + sentiment: "positive", + date: new Date("2023-01-02T15:00:00.000Z"), + }); + expect(result.user.id).toBe(2); + expect(result.shape.id).toBe(2); + expect(result.tags).toEqual([ + { id: 1, name: "Old" }, + { id: 3, name: "긍정" }, + { id: 7, name: "1월" }, + ]); }); it("존재하지 않는 일기 uuid에 대한 요청", async () => { From 79ae20e8056ed10f4ca41bb6c5d186c02204ae3f Mon Sep 17 00:00:00 2001 From: jeongmin Date: Wed, 13 Dec 2023 02:20:28 +0900 Subject: [PATCH 14/18] =?UTF-8?q?test:=20repository=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=97=90=EC=84=9C=20=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/test/int/tags.repository.int-spec.ts | 14 +------------- BE/test/int/user.repository.int-spec.ts | 12 +----------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/BE/test/int/tags.repository.int-spec.ts b/BE/test/int/tags.repository.int-spec.ts index 58917a1..e8f69b7 100644 --- a/BE/test/int/tags.repository.int-spec.ts +++ b/BE/test/int/tags.repository.int-spec.ts @@ -1,9 +1,7 @@ -import { RedisModule } from "@liaoliaots/nestjs-redis"; import { Test, TestingModule } from "@nestjs/testing"; import { TypeOrmModule } from "@nestjs/typeorm"; import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { TagsRepository } from "src/tags/tags.repository"; -import { TagsModule } from "src/tags/tags.module"; import { Tag } from "src/tags/tags.entity"; import { NotFoundException } from "@nestjs/common"; import { TransactionalTestContext } from "typeorm-transactional-tests"; @@ -16,17 +14,7 @@ describe("TagsRepository 통합 테스트", () => { beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [ - TypeOrmModule.forRoot(typeORMTestConfig), - TagsModule, - RedisModule.forRoot({ - readyLog: true, - config: { - host: "223.130.129.145", - port: 6379, - }, - }), - ], + imports: [TypeOrmModule.forRoot(typeORMTestConfig)], providers: [TagsRepository], }).compile(); diff --git a/BE/test/int/user.repository.int-spec.ts b/BE/test/int/user.repository.int-spec.ts index 6a514b7..ac53df1 100644 --- a/BE/test/int/user.repository.int-spec.ts +++ b/BE/test/int/user.repository.int-spec.ts @@ -18,17 +18,7 @@ describe("UsersRepository 통합 테스트", () => { beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [ - TypeOrmModule.forRoot(typeORMTestConfig), - AuthModule, - RedisModule.forRoot({ - readyLog: true, - config: { - host: "223.130.129.145", - port: 6379, - }, - }), - ], + imports: [TypeOrmModule.forRoot(typeORMTestConfig)], providers: [UsersRepository], }).compile(); From 6be3ef3087cdbf5c439bd1b4374ad6be73c35367 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Wed, 13 Dec 2023 12:12:45 +0900 Subject: [PATCH 15/18] =?UTF-8?q?refactor:=20purchaseRepository=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용하지 않는 import문 제거 - user 객체가 아닌 userId를 인자로 받도록 수정 - 반환값 명시 --- BE/src/purchase/purchase.repository.ts | 7 +++---- BE/src/purchase/purchase.service.ts | 5 +++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/BE/src/purchase/purchase.repository.ts b/BE/src/purchase/purchase.repository.ts index 4749de1..55baf81 100644 --- a/BE/src/purchase/purchase.repository.ts +++ b/BE/src/purchase/purchase.repository.ts @@ -1,5 +1,4 @@ import { Purchase } from "./purchase.entity"; -import { PurchaseDesignDto } from "./dto/purchase.design.dto"; import { User } from "src/auth/users.entity"; import { designEnum, domainEnum } from "src/utils/enum"; @@ -9,7 +8,7 @@ export class PurchaseRepository { domain: domainEnum, design: designEnum, ): Promise { - const purchase = await Purchase.create(); + const purchase = Purchase.create(); purchase.domain = domain; purchase.design = design; @@ -18,7 +17,7 @@ export class PurchaseRepository { await purchase.save(); } - async getDesignPurchaseList(user: User) { - return Purchase.find({ where: { user: { userId: user.userId } } }); + async getDesignPurchaseList(userId: string): Promise { + return Purchase.find({ where: { user: { userId } } }); } } diff --git a/BE/src/purchase/purchase.service.ts b/BE/src/purchase/purchase.service.ts index 6898fda..71ad334 100644 --- a/BE/src/purchase/purchase.service.ts +++ b/BE/src/purchase/purchase.service.ts @@ -70,8 +70,9 @@ export class PurchaseService { } async getDesignPurchaseList(user: User): Promise { - const purchaseList = - await this.purchaseRepository.getDesignPurchaseList(user); + const purchaseList = await this.purchaseRepository.getDesignPurchaseList( + user.userId, + ); const groundPurchase = []; purchaseList.forEach((purchase) => { From 7a44d7d91dc9fba5540168e3d70042dba40b579b Mon Sep 17 00:00:00 2001 From: jeongmin Date: Wed, 13 Dec 2023 12:13:09 +0900 Subject: [PATCH 16/18] =?UTF-8?q?test:=20PurchaseRepository=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 테스트 DB 더미데이터 반영 --- BE/test/int/purchase.repository.int-spec.ts | 39 +++++++++++++++++---- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/BE/test/int/purchase.repository.int-spec.ts b/BE/test/int/purchase.repository.int-spec.ts index cf346cd..3204705 100644 --- a/BE/test/int/purchase.repository.int-spec.ts +++ b/BE/test/int/purchase.repository.int-spec.ts @@ -4,6 +4,7 @@ import { User } from "src/auth/users.entity"; import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { Purchase } from "src/purchase/purchase.entity"; import { PurchaseRepository } from "src/purchase/purchase.repository"; +import { designEnum, domainEnum } from "src/utils/enum"; import { DataSource } from "typeorm"; import { TransactionalTestContext } from "typeorm-transactional-tests"; @@ -32,16 +33,42 @@ describe("PurchaseRepository 통합 테스트", () => { await transactionalContext.finish(); }); + describe("purchaseDesign 메서드", () => { + it("신규 유저 메서드 정상 요청", async () => { + const userId = "newUser"; + const newUser = await User.findOne({ where: { userId } }); + + await purchaseRepository.purchaseDesign( + newUser, + domainEnum.GROUND, + designEnum.GROUND_2D, + ); + const result = await Purchase.find({ where: { user: { userId } } }); + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + domain: "ground", + design: "ground_2d", + user: { id: 3 }, + }); + }); + }); + describe("getDesignPurchaseList 메서드", () => { - it("메서드 정상 요청", async () => { - const user = await User.findOne({ where: { userId: "commonUser" } }); + it("기존 유저 메서드 정상 요청", async () => { + const result = await purchaseRepository.getDesignPurchaseList("oldUser"); - const purchaseList = await Purchase.find({ - where: { user: { userId: user.userId } }, + expect(result).toHaveLength(1); + expect(result[0]).toMatchObject({ + id: 1, + domain: "ground", + design: "ground_2d", + user: { id: 2 }, }); - const result = await purchaseRepository.getDesignPurchaseList(user); + }); - expect(result).toStrictEqual(purchaseList); + it("신규 유저 메서드 정상 요청", async () => { + const result = await purchaseRepository.getDesignPurchaseList("newUser"); + expect(result).toEqual([]); }); }); }); From c0235bbbd4ecbf2ddebe530bf8b9126a8b3664ae Mon Sep 17 00:00:00 2001 From: jeongmin Date: Wed, 13 Dec 2023 16:59:06 +0900 Subject: [PATCH 17/18] =?UTF-8?q?test:=20PurchaseService=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 테스트DB 더미데이터 반영 --- BE/test/int/purchase.service.int-spec.ts | 128 ++++++++++++----------- 1 file changed, 68 insertions(+), 60 deletions(-) diff --git a/BE/test/int/purchase.service.int-spec.ts b/BE/test/int/purchase.service.int-spec.ts index b403870..216cdec 100644 --- a/BE/test/int/purchase.service.int-spec.ts +++ b/BE/test/int/purchase.service.int-spec.ts @@ -6,22 +6,16 @@ import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { PurchaseDesignDto } from "src/purchase/dto/purchase.design.dto"; import { PurchaseRepository } from "src/purchase/purchase.repository"; import { PurchaseService } from "src/purchase/purchase.service"; -import { premiumStatus } from "src/utils/enum"; +import { designEnum, domainEnum, premiumStatus } from "src/utils/enum"; import { DataSource } from "typeorm"; import { TransactionalTestContext } from "typeorm-transactional-tests"; describe("PurchaseService 통합 테스트", () => { let purchaseService: PurchaseService; + let purchaseDesignDto: PurchaseDesignDto; let dataSource: DataSource; let transactionalContext: TransactionalTestContext; - const userMockData = { - userId: "PurchaseServiceTest", - password: "PurchaseServiceTest", - nickname: "PurchaseServiceTest", - email: "test@test.com", - }; - beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [TypeOrmModule.forRoot(typeORMTestConfig)], @@ -30,6 +24,10 @@ describe("PurchaseService 통합 테스트", () => { purchaseService = moduleFixture.get(PurchaseService); dataSource = moduleFixture.get(DataSource); + + purchaseDesignDto = new PurchaseDesignDto(); + purchaseDesignDto.domain = domainEnum.GROUND; + purchaseDesignDto.design = designEnum.GROUND_2D; }); beforeEach(async () => { @@ -41,79 +39,89 @@ describe("PurchaseService 통합 테스트", () => { await transactionalContext.finish(); }); - afterAll(async () => {}); - - // // 별가루가 부족한 경우 테스트 - // // 이미 존재하는 디자인에 대한 경우 테스트 - // // 위 두 테스트는 테스트 DB 데이터 삭제 오류의 문제로 테스트 DB 독립화 이후 구현 예정 - describe("purchaseDesign & getDesignPurchaseList 메서드", () => { - it("메서드 정상 요청", async () => { - // 테스트 DB 독립 시 수정 필요 - const user = User.create({ - ...userMockData, - premium: premiumStatus.FALSE, - credit: 500, - }); - - await user.save(); - - const purchaseDesignDto = new PurchaseDesignDto(); - purchaseDesignDto.domain = "GROUND"; - purchaseDesignDto.design = "GROUND_GREEN"; + it("네이버유저 메서드 정상 요청", async () => { + const naverUser = await User.findOne({ where: { userId: "naverUser" } }); + { + const result = await purchaseService.getDesignPurchaseList(naverUser); + expect(result).toStrictEqual({ + ground: [], + }); + } const { credit } = await purchaseService.purchaseDesign( - user, + naverUser, purchaseDesignDto, ); expect(credit).toBe(0); - const result = await purchaseService.getDesignPurchaseList(user); + { + const result = await purchaseService.getDesignPurchaseList(naverUser); + expect(result).toStrictEqual({ + ground: ["ground_2d"], + }); + } + }); + + it("크레딧이 없는 신규유저 요청", async () => { + const newUser = await User.findOne({ where: { userId: "newUser" } }); + + const result = await purchaseService.getDesignPurchaseList(newUser); expect(result).toStrictEqual({ - ground: ["#254117"], - sky: [], + ground: [], }); + + await expect( + purchaseService.purchaseDesign(newUser, purchaseDesignDto), + ).rejects.toThrow(`보유한 별가루가 부족합니다. 현재 0 별가루`); }); - }); - describe("purchasePremium 메서드", () => { - it("프리미엄 구매 성공", async () => { - const user = User.create({ - ...userMockData, - premium: premiumStatus.FALSE, - credit: 500, - }); + it("이미 ground_2d 디자인을 구매한 기존유저 요청", async () => { + const oldUser = await User.findOne({ where: { userId: "oldUser" } }); - await user.save(); + const result = await purchaseService.getDesignPurchaseList(oldUser); + expect(result).toStrictEqual({ + ground: ["ground_2d"], + }); - const result = await purchaseService.purchasePremium(user); + await expect( + purchaseService.purchaseDesign(oldUser, purchaseDesignDto), + ).rejects.toThrow(`이미 구매한 디자인입니다.`); + }); + }); - expect(result.credit).toBe(150); - expect(user.premium).toBe(premiumStatus.TRUE); + describe("purchasePremium & getPremiumStatus 메서드", () => { + it("네이버유저 프리미엄 구매 성공", async () => { + const naverUser = await User.findOne({ where: { userId: "naverUser" } }); + { + const { premium } = await purchaseService.getPremiumStatus(naverUser); + expect(premium).toBe(premiumStatus.FALSE); + } + + const { credit } = await purchaseService.purchasePremium(naverUser); + expect(credit).toBe(150); + { + const { premium } = await purchaseService.getPremiumStatus(naverUser); + expect(premium).toBe(premiumStatus.TRUE); + } }); - it("크레딧 부족으로 구매 실패", async () => { - const user = User.create({ - ...userMockData, - premium: premiumStatus.FALSE, - credit: 300, - }); - await user.save(); + it("크레딧이 없는 신규유저 요청", async () => { + const newUser = await User.findOne({ where: { userId: "newUser" } }); - await expect(purchaseService.purchasePremium(user)).rejects.toThrow( - `보유한 별가루가 부족합니다. 현재 ${user.credit} 별가루`, + await expect(purchaseService.purchasePremium(newUser)).rejects.toThrow( + `보유한 별가루가 부족합니다. 현재 0 별가루`, ); }); - it("프리미엄 중복 구매시 실패", async () => { - const user = User.create({ - ...userMockData, - premium: premiumStatus.TRUE, - credit: 500, - }); - await user.save(); + it("이미 프리미엄 사용자인 카카오유저 요청", async () => { + const kakaoUser = await User.findOne({ where: { userId: "kakaoUser" } }); + { + const { premium } = await purchaseService.getPremiumStatus(kakaoUser); + expect(premium).toBe(premiumStatus.TRUE); + } - await expect(purchaseService.purchasePremium(user)).rejects.toThrow( + await expect(purchaseService.purchasePremium(kakaoUser)).rejects.toThrow( "이미 프리미엄 사용자입니다.", ); }); From cbf0a3284ab0f7a782616eaeb645e2bbefee2259 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Wed, 13 Dec 2023 18:42:20 +0900 Subject: [PATCH 18/18] =?UTF-8?q?test:=20DiariesService=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용하지 않는 모듈 제거 - beforeAll에 중복되는 모킹 추가 - afterAll에 restoreAllMocks 함수 호출 - 테스트 DB 더미 데이터 반영 --- BE/test/int/diaries.service.int-spec.ts | 294 +++++++++++------------- 1 file changed, 129 insertions(+), 165 deletions(-) diff --git a/BE/test/int/diaries.service.int-spec.ts b/BE/test/int/diaries.service.int-spec.ts index 3fff809..a61afea 100644 --- a/BE/test/int/diaries.service.int-spec.ts +++ b/BE/test/int/diaries.service.int-spec.ts @@ -1,10 +1,8 @@ -import { RedisModule } from "@liaoliaots/nestjs-redis"; import { Test, TestingModule } from "@nestjs/testing"; import { TypeOrmModule } from "@nestjs/typeorm"; import { User } from "src/auth/users.entity"; import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { Diary } from "src/diaries/diaries.entity"; -import { DiariesModule } from "src/diaries/diaries.module"; import { DiariesRepository } from "src/diaries/diaries.repository"; import { CreateDiaryDto, @@ -24,121 +22,104 @@ import { createCipheriv, createDecipheriv } from "crypto"; import { of } from "rxjs"; import { DataSource } from "typeorm"; import { TransactionalTestContext } from "typeorm-transactional-tests"; +import { UsersRepository } from "src/auth/users.repository"; +import { NotFoundException } from "@nestjs/common"; describe("DiariesService 통합 테스트", () => { let diariesService: DiariesService; - let tagsRepository: TagsRepository; + let diariesRepository: DiariesRepository; let sentimentDto: SentimentDto; let httpService: HttpService; - let user: User; - let shape: Shape; - let createResult: Diary; let dataSource: DataSource; let transactionalContext: TransactionalTestContext; - jest.mock("rxjs", () => ({ - lastValueFrom: jest.fn().mockResolvedValue({ - data: { - document: { - confidence: { - positive: 0.1, - neutral: 0.2, - negative: 0.7, - }, - sentiment: "negative", - }, - }, - }), - })); - beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [ - TypeOrmModule.forRoot(typeORMTestConfig), - HttpModule, - DiariesModule, - RedisModule.forRoot({ - readyLog: true, - config: { - host: "223.130.129.145", - port: 6379, - }, - }), - ], + imports: [TypeOrmModule.forRoot(typeORMTestConfig), HttpModule], providers: [ DiariesService, DiariesRepository, TagsRepository, ShapesRepository, - { - provide: HttpService, - useValue: { - post: jest.fn(), - }, - }, + UsersRepository, ], }).compile(); diariesService = moduleFixture.get(DiariesService); - tagsRepository = moduleFixture.get(TagsRepository); + diariesRepository = moduleFixture.get(DiariesRepository); httpService = moduleFixture.get(HttpService); dataSource = moduleFixture.get(DataSource); - }); - beforeEach(async () => { - transactionalContext = new TransactionalTestContext(dataSource); - await transactionalContext.start(); - - sentimentDto = new SentimentDto(); - sentimentDto.positiveRatio = 0; - sentimentDto.negativeRatio = 0; - sentimentDto.neutralRatio = 100; - sentimentDto.sentiment = sentimentStatus.NEUTRAL; - user = await User.findOne({ where: { userId: "commonUser" } }); - shape = await Shape.findOne({ - where: { user: { userId: "commonUser" } }, - }); + sentimentDto = new SentimentDto(0, 100, 0, sentimentStatus.NEUTRAL); - jest - .spyOn(diariesService, "getTags") - .mockResolvedValueOnce([new Tag(), new Tag()]); jest .spyOn(diariesService, "getSentiment") .mockResolvedValueOnce(sentimentDto); - const createDiaryDto = new CreateDiaryDto(); - createDiaryDto.title = "Test Title"; - createDiaryDto.content = "Test Content"; - createDiaryDto.point = "1,1,1"; - (createDiaryDto.date as any) = "2023-11-29"; - createDiaryDto.tags = ["tag1", "tag2"]; - createDiaryDto.shapeUuid = shape.uuid; + const mockData: any = { + status: 200, + statusText: "OK", + headers: {}, + config: {}, + data: { + document: { + confidence: { + positive: 10.0, + neutral: 20.0, + negative: 70.0, + }, + sentiment: "negative", + }, + }, + }; + jest.spyOn(httpService, "post").mockImplementation(() => of(mockData)); + }); - createResult = await diariesService.writeDiary(createDiaryDto, user); + beforeEach(async () => { + transactionalContext = new TransactionalTestContext(dataSource); + await transactionalContext.start(); }); afterEach(async () => { await transactionalContext.finish(); + }); + + afterAll(async () => { await jest.restoreAllMocks(); }); describe("writeDiary 통합 테스트", () => { - it("메서드 정상 요청", async () => { - expect(createResult).toBeInstanceOf(Diary); - expect(createResult.title).toBe("Test Title"); - expect(createResult).toMatchObject({ + it("신규 유저 메서드 정상 요청", async () => { + const newUser = await User.findOne({ where: { userId: "newUser" } }); + const shape = await Shape.findOne({ where: { id: 2 } }); + + const createDiaryDto = new CreateDiaryDto(); + createDiaryDto.title = "Test Title"; + createDiaryDto.content = "Test Content"; + createDiaryDto.point = "1,1,1"; + createDiaryDto.date = new Date("2023-11-29"); + createDiaryDto.tags = ["Naver"]; + createDiaryDto.shapeUuid = shape.uuid; + + const result = await diariesService.writeDiary(createDiaryDto, newUser); + expect(result).toBeInstanceOf(Diary); + expect(result).toMatchObject({ title: "Test Title", - content: "f2302664806f9f519404b1d583902d36", positiveRatio: 0, negativeRatio: 0, neutralRatio: 100, sentiment: "neutral", - date: "2023-11-29", + date: new Date("2023-11-29"), point: "1,1,1", - user: { id: 1 }, - shape: { id: 1 }, + user: { id: 3 }, + shape: { id: 2 }, deletedDate: null, }); + + const decryptedContent = await diariesService.getDecryptedContent( + result.content, + ); + expect(decryptedContent).toBe("Test Content"); }); }); @@ -179,101 +160,122 @@ describe("DiariesService 통합 테스트", () => { describe("readDiary 통합 테스트", () => { it("메서드 정상 요청", async () => { const readDiaryDto = new ReadDiaryDto(); - readDiaryDto.uuid = createResult.uuid; - - jest - .spyOn(diariesService, "getDecryptedContent") - .mockResolvedValue("decrypted"); + readDiaryDto.uuid = "0c388311-61c4-41ad-8d4d-b7b1ce075ef2"; const result = await diariesService.readDiary(readDiaryDto); expect(result).toBeInstanceOf(Diary); - expect(result.title).toBe("Test Title"); + expect(result).toMatchObject({ + title: "부정 1", + content: "너무 싫어", + uuid: "0c388311-61c4-41ad-8d4d-b7b1ce075ef2", + positiveRatio: 0.00949954, + negativeRatio: 99.978, + neutralRatio: 0.0124967, + sentiment: "negative", + date: new Date("2023-03-03"), + tags: [ + { id: 1, name: "Old" }, + { id: 5, name: "9월" }, + { id: 8, name: "중립" }, + ], + }); }); }); describe("readDiariesByUser 통합 테스트", () => { it("메서드 정상 요청", async () => { - jest - .spyOn(diariesService, "getDecryptedContent") - .mockResolvedValue("decrypted2323"); - - const result = await diariesService.readDiariesByUser(user); - - expect(result[0]).toBeInstanceOf(Diary); + const naverUser = await User.findOne({ where: { userId: "naverUser" } }); + const diaries = await diariesService.readDiariesByUser(naverUser); + + const expectedValues = [ + { title: "긍정 네이버 1", content: "네이버는 너무 좋아" }, + { title: "긍정 네이버 2", content: "네이버는 너무 행복해" }, + { title: "긍정 네이버 3", content: "행복해" }, + ]; + + expect( + diaries.map((diary) => ({ + title: diary.title, + content: diary.content, + })), + ).toEqual(expectedValues); }); }); describe("modifyDiary 통합 테스트", () => { it("메서드 정상 요청", async () => { + const oldUser = await User.findOne({ where: { userId: "oldUser" } }); + const shape = await Shape.findOne({ where: { id: 3 } }); + const updateDiaryDto = new UpdateDiaryDto(); updateDiaryDto.title = "Test Title"; - updateDiaryDto.content = "Test Content"; + updateDiaryDto.content = "Updated Content"; updateDiaryDto.point = "3,3,3"; - (updateDiaryDto.date as any) = "2023-11-29"; - updateDiaryDto.tags = ["tag1", "tag2"]; + updateDiaryDto.date = new Date("2023-11-29"); + updateDiaryDto.tags = ["Naver"]; updateDiaryDto.shapeUuid = shape.uuid; - updateDiaryDto.uuid = createResult.uuid; + updateDiaryDto.uuid = "0c388311-61c4-41ad-8d4d-b7b1ce075ef2"; - const result = await diariesService.modifyDiary(updateDiaryDto, user); + const result = await diariesService.modifyDiary(updateDiaryDto, oldUser); expect(result).toBeInstanceOf(Diary); - expect(result.point).toBe("3,3,3"); + expect(result).toMatchObject({ + title: "Test Title", + uuid: "0c388311-61c4-41ad-8d4d-b7b1ce075ef2", + positiveRatio: 10, + neutralRatio: 20, + negativeRatio: 70, + sentiment: "negative", + date: new Date("2023-11-29"), + tags: [{ id: 13, name: "Naver" }], + point: "3,3,3", + }); + + const decryptedContent = await diariesService.getDecryptedContent( + result.content, + ); + expect(decryptedContent).toBe("Updated Content"); }); }); describe("deleteDiary 통합 테스트", () => { it("메서드 정상 요청", async () => { const deleteDiaryDto = new DeleteDiaryDto(); - deleteDiaryDto.uuid = createResult.uuid; + deleteDiaryDto.uuid = "0c388311-61c4-41ad-8d4d-b7b1ce075ef2"; await diariesService.deleteDiary(deleteDiaryDto); + + try { + await diariesRepository.getDiaryByUuid(deleteDiaryDto.uuid); + } catch (error) { + expect(error).toBeInstanceOf(NotFoundException); + expect(error.message).toBe("존재하지 않는 일기입니다."); + } }); }); describe("getTags 통합 테스트", () => { it("메서드 정상 요청", async () => { - const tagNames = ["tag", "tag"]; - const tag = new Tag(); - tag.name = "tag"; - - jest.spyOn(tagsRepository, "getTagByName").mockResolvedValue(tag); - jest.spyOn(tagsRepository, "createTag").mockResolvedValue(tag); + const tagNames = ["3월", "5월", "7월", "13월"]; const result = await diariesService.getTags(tagNames); expect(result[0]).toBeInstanceOf(Tag); - expect(result[0].name).toBe("tag"); + expect(result.map((tag) => tag.name)).toEqual( + expect.arrayContaining(tagNames), + ); }); }); describe("getSentimentByContent 통합 테스트", () => { it("메서드 정상 요청", async () => { - const result: any = { - status: 200, - statusText: "OK", - headers: {}, - config: {}, - data: { - document: { - confidence: { - positive: 0.1, - neutral: 0.2, - negative: 0.7, - }, - sentiment: "negative", - }, - }, - }; - - jest.spyOn(httpService, "post").mockImplementation(() => of(result)); - const sentimentDto = await diariesService.getSentimentByContent("test"); expect(sentimentDto).toEqual({ - positiveRatio: 0.1, - neutralRatio: 0.2, - negativeRatio: 0.7, + positiveRatio: 10, + neutralRatio: 20, + negativeRatio: 70, sentiment: "negative", }); }); @@ -281,59 +283,21 @@ describe("DiariesService 통합 테스트", () => { describe("getSentiment 통합 테스트", () => { it("1000자 이하 메서드 정상 요청", async () => { - const result: any = { - status: 200, - statusText: "OK", - headers: {}, - config: {}, - data: { - document: { - confidence: { - positive: 0.1, - neutral: 0.2, - negative: 0.7, - }, - sentiment: "negative", - }, - }, - }; - - jest.spyOn(httpService, "post").mockImplementation(() => of(result)); - const sentimentDto = await diariesService.getSentiment("test"); expect(sentimentDto).toEqual({ - positiveRatio: 0.1, - neutralRatio: 0.2, - negativeRatio: 0.7, + positiveRatio: 10, + neutralRatio: 20, + negativeRatio: 70, sentiment: "negative", }); }); it("1000자 초과 메서드 정상 요청", async () => { - const result: any = { - status: 200, - statusText: "OK", - headers: {}, - config: {}, - data: { - document: { - confidence: { - positive: 0.1, - neutral: 0.2, - negative: 0.7, - }, - sentiment: "negative", - }, - }, - }; - - jest.spyOn(httpService, "post").mockImplementation(() => of(result)); - const sentimentDto = await diariesService.getSentiment("a".repeat(1500)); - expect(sentimentDto.negativeRatio).toBeGreaterThan(0.69); - expect(sentimentDto.negativeRatio).toBeLessThan(0.71); + expect(sentimentDto.negativeRatio).toBeGreaterThan(69); + expect(sentimentDto.negativeRatio).toBeLessThan(71); expect(sentimentDto.sentiment).toBe("negative"); }); });