From b635bde3aec72db9f85bff67d810af2a619cea75 Mon Sep 17 00:00:00 2001 From: catensia Date: Mon, 12 Dec 2022 13:06:30 +0900 Subject: [PATCH 1/2] chore: Update path to relative --- server/src/recruit/recruit.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/recruit/recruit.service.ts b/server/src/recruit/recruit.service.ts index 57b2565..1b5b9ff 100644 --- a/server/src/recruit/recruit.service.ts +++ b/server/src/recruit/recruit.service.ts @@ -4,14 +4,14 @@ import { CreateRecruitRequestDto } from "./dto/request/create-recruit.request"; import { GetRecruitsRequestDto } from "./dto/request/get-recruits.request"; import { UserRecruitRepository } from "../common/repositories/user_recruit.repository"; import { plainToGetRecruitDto } from "../common/utils/plainToGetRecruitDto"; -import { DataSource, getConnection } from "typeorm"; +import { DataSource } from "typeorm"; import { plainToInstance } from "class-transformer"; import { JoinRecruitRequestDto } from "./dto/request/join-recruit.request"; import { Recruit } from "../common/entities/recruit.entity"; import { DeleteRecruitRequestDto } from "./dto/request/delete-recruit.request"; -import { UserRecruit } from "src/common/entities/user_recruit.entity"; +import { UserRecruit } from "../common/entities/user_recruit.entity"; import { UnjoinRecruitRequestDto } from "./dto/request/unjoin-recruit.request"; -import { UserRepository } from "src/common/repositories/user.repository"; +import { UserRepository } from "../common/repositories/user.repository"; @Injectable() export class RecruitService { From 6c7d6935aa8f9afa08186bf6dde45300fe3cd36a Mon Sep 17 00:00:00 2001 From: catensia Date: Tue, 13 Dec 2022 01:49:05 +0900 Subject: [PATCH 2/2] feat: refactor previous test codes and add test codes for user --- server/package.json | 3 +- server/test/app.e2e-spec.ts | 310 ++++++++++++++------------- server/test/data/expectedData.ts | 23 ++ server/test/data/mockData.ts | 40 ++++ server/test/data/requestData.ts | 6 + server/test/helpers/users.ts | 0 server/test/types/courseResponse.ts | 0 server/test/types/coursesResponse.ts | 0 8 files changed, 232 insertions(+), 150 deletions(-) create mode 100644 server/test/data/expectedData.ts create mode 100644 server/test/data/mockData.ts create mode 100644 server/test/data/requestData.ts create mode 100644 server/test/helpers/users.ts create mode 100644 server/test/types/courseResponse.ts create mode 100644 server/test/types/coursesResponse.ts diff --git a/server/package.json b/server/package.json index 84505a4..530762b 100644 --- a/server/package.json +++ b/server/package.json @@ -18,7 +18,8 @@ "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json --forceExit --verbose=false" + "test:e2e": "jest --config ./test/jest-e2e.json --forceExit --verbose=false", + "test:e2e-silent": "jest --config ./test/jest-e2e.json --forceExit --silent=false" }, "dependencies": { "@nestjs/axios": "^1.0.0", diff --git a/server/test/app.e2e-spec.ts b/server/test/app.e2e-spec.ts index acc23de..d35198f 100644 --- a/server/test/app.e2e-spec.ts +++ b/server/test/app.e2e-spec.ts @@ -7,6 +7,7 @@ import { CallHandler, } from "@nestjs/common"; import { Reflector } from "@nestjs/core"; +import { AxiosResponse } from "axios"; import * as request from "supertest"; import * as cookieParser from "cookie-parser"; import { AppModule } from "./../src/app.module"; @@ -14,9 +15,13 @@ import { HttpRequestBodyInterceptor } from "../src/common/interceptors/http-requ import { DataSource } from "typeorm"; import { HDong } from "../src/common/entities/h_dong.entity"; import { query } from "./config/query"; -import { Observable } from "rxjs"; +import { Observable, of } from "rxjs"; import { CustomJwtService } from "../src/common/modules/custom-jwt/custom-jwt.service"; import { JwtService } from "@nestjs/jwt"; +import { HttpService } from "@nestjs/axios"; +import { mockAxiosResponse, mockCourse, mockRecruit, mockUserNoEmail } from "./data/mockData"; +import { expectedCourseData, expectedRecruitData } from "./data/expectedData"; +import { requestRecruitMockData } from "./data/requestData"; describe("AppController (e2e)", () => { let app: INestApplication; @@ -24,7 +29,7 @@ describe("AppController (e2e)", () => { let interceptor: HttpRequestBodyInterceptor; let customJwtService: CustomJwtService; let jwtService: JwtService; - + let httpService: HttpService; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], @@ -45,30 +50,44 @@ describe("AppController (e2e)", () => { customJwtService = moduleFixture.get(CustomJwtService); jwtService = moduleFixture.get(JwtService); dataSource.getRepository(HDong).query(query); + httpService = moduleFixture.get(HttpService); await app.init(); }); + const mockInterceptor = (userId: number, type: string) => { + return jest + .spyOn(interceptor, "intercept") + .mockImplementation((ctxt: ExecutionContext, next: CallHandler): Observable => { + const request = ctxt.switchToHttp().getRequest(); + if (type === "get" || type === "delete") request.params.userId = userId; + else request.body.userId = userId; + return next.handle(); + }); + }; + const mockInterceptorOnce = (userId: number, type: string) => { + return jest + .spyOn(interceptor, "intercept") + .mockImplementationOnce((ctxt: ExecutionContext, next: CallHandler): Observable => { + const request = ctxt.switchToHttp().getRequest(); + if (type === "get" || type === "delete") request.params.userId = userId; + else request.body.userId = userId; + return next.handle(); + }); + }; + const arr = Array.from(Array(15).keys()); + describe("UserController test", () => { describe("/user (POST): sign up", () => { it("sign up user with valid data", async () => { - await request(app.getHttpServer()) - .post("/user") - .send({ userId: "June1010", password: "1234567890", pace: 300, hCode: "1100000000" }) - .expect(201); // Created + await request(app.getHttpServer()).post("/user").send(mockUserNoEmail("June1010")).expect(201); // Created }); it("sign up user with duplicate userId", async () => { - await request(app.getHttpServer()) - .post("/user") - .send({ userId: "June1010", password: "1234567890", pace: 300, hCode: "1100000000" }) - .expect(400); // Bad Request + await request(app.getHttpServer()).post("/user").send(mockUserNoEmail("June1010")).expect(400); // Bad Request }); it("sign up user with id including symbols, expecting validator exception", async () => { - await request(app.getHttpServer()) - .post("/user") - .send({ userId: "June10!!", password: "1234567890", pace: 300, hCode: "1100000000" }) - .expect(400); + await request(app.getHttpServer()).post("/user").send(mockUserNoEmail("June1010")).expect(400); }); }); @@ -88,26 +107,8 @@ describe("AppController (e2e)", () => { describe("CourseController test", () => { describe("/course (POST)", () => { it("create course with valid data", async () => { - const mock = jest - .spyOn(interceptor, "intercept") - .mockImplementationOnce((ctxt: ExecutionContext, next: CallHandler): Observable => { - const request = ctxt.switchToHttp().getRequest(); - request.body.userId = 1; // June1010 - return next.handle(); - }); - - const response = await request(app.getHttpServer()) - .post("/course") - .send({ - title: "test example", - path: [ - { lat: 1, lng: 2 }, - { lat: 2, lng: 3 }, - ], - pathLength: 300, - hCode: "1100000000", - }); - + const mock = mockInterceptorOnce(1, "post"); + const response = await request(app.getHttpServer()).post("/course").send(mockCourse("test example")); expect(response.body.statusCode).toEqual(201); expect(response.body.data.courseId).toEqual(1); mock.mockRestore(); @@ -121,32 +122,17 @@ describe("AppController (e2e)", () => { expect(response.body.statusCode).toEqual(200); expect(response.body.data).toBeInstanceOf(Array); expect(response.body.data.length).toEqual(1); - expect(response.body.data[0].id).toEqual(1); - expect(response.body.data[0].title).toEqual("test example"); - expect(response.body.data[0].path).toEqual([ - { lat: 1, lng: 2 }, - { lat: 2, lng: 3 }, - ]); - expect(response.body.data[0].pathLength).toEqual(300); - expect(response.body.data[0].userId).toEqual("June1010"); - expect(response.body.data[0].hDong).toEqual({ name: "서울특별시" }); + const { createdAt, ...responseCourseData } = response.body.data[0]; + expect(responseCourseData).toEqual(expectedCourseData); }); it("get course list with query='test example'", async () => { const response = await request(app.getHttpServer()).get("/course?query=test example").expect(200); - expect(response.body.statusCode).toEqual(200); expect(response.body.data).toBeInstanceOf(Array); expect(response.body.data.length).toEqual(1); - expect(response.body.data[0].id).toEqual(1); - expect(response.body.data[0].title).toEqual("test example"); - expect(response.body.data[0].path).toEqual([ - { lat: 1, lng: 2 }, - { lat: 2, lng: 3 }, - ]); - expect(response.body.data[0].pathLength).toEqual(300); - expect(response.body.data[0].userId).toEqual("June1010"); - expect(response.body.data[0].hDong).toEqual({ name: "서울특별시" }); + const { createdAt, ...responseCourseData } = response.body.data[0]; + expect(responseCourseData).toEqual(expectedCourseData); }); }); @@ -156,39 +142,21 @@ describe("AppController (e2e)", () => { expect(response.body.statusCode).toEqual(200); expect(response.body.data).toBeInstanceOf(Object); expect(response.body.data.id).toEqual(1); - expect(response.body.data.title).toEqual("test example"); - expect(response.body.data.path).toEqual([ - { lat: 1, lng: 2 }, - { lat: 2, lng: 3 }, - ]); - expect(response.body.data.pathLength).toEqual(300); - expect(response.body.data.userId).toEqual("June1010"); - expect(response.body.data.hDong).toEqual({ name: "서울특별시" }); + const { createdAt, ...responseCourseData } = response.body.data; + expect(responseCourseData).toEqual(expectedCourseData); }); }); }); describe("RecruitController test", () => { - const startTime = new Date(2037, 11, 30).toISOString(); - describe("/recruit (POST)", () => { it("create recruit", async () => { - const mock = jest - .spyOn(interceptor, "intercept") - .mockImplementationOnce((ctxt: ExecutionContext, next: CallHandler): Observable => { - const request = ctxt.switchToHttp().getRequest(); - request.body.userId = 1; // June1010 - return next.handle(); - }); - + const mock = mockInterceptorOnce(1, "post"); const response = await request(app.getHttpServer()) .post("/recruit") .send({ - title: "test example", - startTime, - maxPpl: 1, - pace: 300, courseId: 1, + ...requestRecruitMockData, }) .expect(201); @@ -205,34 +173,16 @@ describe("AppController (e2e)", () => { expect(response.body.statusCode).toEqual(200); expect(response.body.data).toBeInstanceOf(Array); expect(response.body.data.length).toEqual(1); - expect(response.body.data[0].id).toEqual(1); - expect(response.body.data[0].title).toEqual("test example"); - expect(response.body.data[0].startTime).toEqual("2037-12-29T15:00:00.000Z"); - expect(response.body.data[0].maxPpl).toEqual(1); - expect(response.body.data[0].currentPpl).toEqual(1); - expect(response.body.data[0].userId).toEqual("June1010"); - expect(response.body.data[0].pace).toEqual(300); - expect(response.body.data[0].course.id).toEqual(1); - expect(response.body.data[0].course.title).toEqual("test example"); - expect(response.body.data[0].course.path).toEqual([ - { lat: 1, lng: 2 }, - { lat: 2, lng: 3 }, - ]); - expect(response.body.data[0].course.pathLength).toEqual(300); - expect(response.body.data[0].course.userId).toEqual("June1010"); - expect(response.body.data[0].course.hDong).toEqual({ name: "서울특별시" }); + const { course, createdAt: createdRecruitAt, ...recruitResponseNoCourse } = response.body.data[0]; + expect(recruitResponseNoCourse).toEqual(expectedRecruitData); + const { createdAt: createdCourseAt, ...courseWithOutCreatedAt } = course; + expect(courseWithOutCreatedAt).toEqual(expectedCourseData); }); }); describe("/recruit/:recruitId (GET)", () => { it("get recruit detail", async () => { - const mock = jest - .spyOn(interceptor, "intercept") - .mockImplementationOnce((ctxt: ExecutionContext, next: CallHandler): Observable => { - const request = ctxt.switchToHttp().getRequest(); - request.params.userId = 1; // June1010 - return next.handle(); - }); + const mock = mockInterceptorOnce(1, "get"); const response = await request(app.getHttpServer()).get("/recruit/1").expect(200); expect(response.body.statusCode).toEqual(200); @@ -243,13 +193,7 @@ describe("AppController (e2e)", () => { describe("/recruit/join (POST)", () => { it("join not existing recruit", async () => { - const mock = jest - .spyOn(interceptor, "intercept") - .mockImplementationOnce((ctxt: ExecutionContext, next: CallHandler): Observable => { - const request = ctxt.switchToHttp().getRequest(); - request.body.userId = 1; // June1010 - return next.handle(); - }); + const mock = mockInterceptorOnce(1, "post"); const response = await request(app.getHttpServer()) .post("/recruit/join") @@ -263,14 +207,7 @@ describe("AppController (e2e)", () => { }); it("join own recruit", async () => { - const mock = jest - .spyOn(interceptor, "intercept") - .mockImplementationOnce((ctxt: ExecutionContext, next: CallHandler): Observable => { - const request = ctxt.switchToHttp().getRequest(); - request.body.userId = 1; // June1010 - return next.handle(); - }); - + const mock = mockInterceptorOnce(1, "post"); const response = await request(app.getHttpServer()) .post("/recruit/join") .send({ recruitId: 1 }) @@ -283,19 +220,9 @@ describe("AppController (e2e)", () => { }); it("join max cap reached recruit", async () => { - await request(app.getHttpServer()) - .post("/user") - .send({ userId: "June1234", password: "1234567890", pace: 300, hCode: "1100000000" }) - .expect(201); // Create another user + await request(app.getHttpServer()).post("/user").send(mockUserNoEmail("June1234")).expect(201); // Create another user - const mock = jest - .spyOn(interceptor, "intercept") - .mockImplementation((ctxt: ExecutionContext, next: CallHandler): Observable => { - const request = ctxt.switchToHttp().getRequest(); - request.body.userId = 2; - - return next.handle(); - }); + const mock = mockInterceptor(2, "post"); let response = await request(app.getHttpServer()) .post("/recruit/join") @@ -311,32 +238,15 @@ describe("AppController (e2e)", () => { }); it("join same recruit twice", async () => { - let mock = jest - .spyOn(interceptor, "intercept") - .mockImplementationOnce((ctxt: ExecutionContext, next: CallHandler): Observable => { - const request = ctxt.switchToHttp().getRequest(); - request.body.userId = 1; // June1010 - return next.handle(); - }); + let mock = mockInterceptorOnce(1, "post"); + await request(app.getHttpServer()).post("/recruit").send(mockRecruit).expect(201); - await request(app.getHttpServer()) - .post("/recruit") - .send({ - title: "test example", - startTime, - maxPpl: 5, - pace: 300, - courseId: 1, - }) - .expect(201); + mock = mockInterceptor(2, "post"); - mock = jest - .spyOn(interceptor, "intercept") - .mockImplementation((ctxt: ExecutionContext, next: CallHandler): Observable => { - const request = ctxt.switchToHttp().getRequest(); - request.body.userId = 2; // June1010 - return next.handle(); - }); + const mockResponse: AxiosResponse = mockAxiosResponse(201); + const emailResponseMock = jest + .spyOn(httpService, "post") + .mockImplementationOnce(() => of(mockResponse)); let response = await request(app.getHttpServer()) .post("/recruit/join") @@ -344,10 +254,12 @@ describe("AppController (e2e)", () => { .expect(201); response = await request(app.getHttpServer()).post("/recruit/join").send({ recruitId: 2 }).expect(201); + expect(response.body.statusCode).toEqual(423); expect(response.body.message).toEqual("이미 참여중인 게시글입니다"); expect(response.body.error).toEqual("Locked"); mock.mockRestore(); + emailResponseMock.mockRestore(); }); }); }); @@ -407,6 +319,106 @@ describe("AppController (e2e)", () => { }); }); + describe("User Controller test", () => { + describe("/user/me/course", () => { + it("test user profile", async () => { + const mock = mockInterceptor(2, "get"); + const response = await request(app.getHttpServer()).get("/user/me"); + const expectedResponseData = { + userId: "June1234", + hDong: { name: "서울특별시" }, + pace: 300, + }; + expect(response.body.statusCode).toEqual(200); + expect(response.body.data).toEqual(expectedResponseData); + mock.mockRestore(); + }); + }); + + describe("/user/me/course", () => { + it("test courses by user", async () => { + const arr = Array.from(Array(15).keys()); + // create user A and B + await request(app.getHttpServer()).post("/user").send(mockUserNoEmail("testaccountA")).expect(201); + await request(app.getHttpServer()).post("/user").send(mockUserNoEmail("testaccountB")).expect(201); + let mock = mockInterceptor(3, "post"); + for await (const i of arr.slice(0, 10)) { + // upload 10 course with user A + const response = await request(app.getHttpServer()) + .post("/course") + .send(mockCourse(`course by A${i}`)); + expect(response.body.statusCode).toEqual(201); + } + + mock = mockInterceptor(4, "post"); + for await (const i of arr) { + // upload 15 course with user B + const response = await request(app.getHttpServer()) + .post("/course") + .send(mockCourse(`course by B${i}`)); + expect(response.body.statusCode).toEqual(201); + } + + //request courses uploaded by userA (which is 10) + mock = mockInterceptorOnce(3, "get"); + const userCoursesResponse = await request(app.getHttpServer()).get("/user/me/course"); + expect(userCoursesResponse.statusCode).toEqual(200); + expect(userCoursesResponse.body.data).toBeInstanceOf(Array); + expect(userCoursesResponse.body.data.length).toEqual(10); // OK + + mock.mockRestore(); + }); + }); + + describe("/user/me/recruit", () => { + it("test recruits by user", async () => { + let mock = mockInterceptor(3, "post"); + for await (const i of arr.slice(1, 8)) { + //post 7 recruits of courseId 1 ~ 7 with user A + await request(app.getHttpServer()) + .post("/recruit") + .send({ courseId: i, ...{ ...requestRecruitMockData, maxPpl: 10 } }) + .expect(201); + } + + mock = mockInterceptor(4, "post"); + for await (const i of arr.slice(8, 11)) { + //post 3 recruits of courseId 8 ~ 10 with user B + await request(app.getHttpServer()) + .post("/recruit") + .send({ courseId: i, ...{ ...requestRecruitMockData, maxPpl: 10 } }) + .expect(201); + } + + // test how many recruits are attended by user A (expected 7) + mock = mockInterceptor(3, "get"); + let userARecruitsResponse = await request(app.getHttpServer()).get("/user/me/recruit"); + expect(userARecruitsResponse.statusCode).toEqual(200); + expect(userARecruitsResponse.body.data.length).toEqual(7); // OK + + //join userB's recruits with userA (recruitID: 10 ~ 12) + + //login with user A with post mode + mock = mockInterceptor(3, "post"); + //prepare axios email mock response + const mockResponse: AxiosResponse = mockAxiosResponse(201); + const emailResponseMock = jest.spyOn(httpService, "post").mockImplementation(() => of(mockResponse)); + for await (const i of arr.slice(10, 13)) { + await request(app.getHttpServer()).post("/recruit/join").send({ recruitId: i }).expect(201); + } + + // finally, check how many recruits are attended by user A (expected 10) + mock = mockInterceptor(3, "get"); + userARecruitsResponse = await request(app.getHttpServer()).get("/user/me/recruit"); + expect(userARecruitsResponse.statusCode).toEqual(200); + expect(userARecruitsResponse.body.data.length).toEqual(10); // OK + + mock.mockRestore(); + emailResponseMock.mockRestore(); + }); + }); + }); + afterAll(async () => { await dataSource.dropDatabase(); await app.close(); diff --git a/server/test/data/expectedData.ts b/server/test/data/expectedData.ts new file mode 100644 index 0000000..24ee5ee --- /dev/null +++ b/server/test/data/expectedData.ts @@ -0,0 +1,23 @@ +export const expectedCourseData = { + id: 1, + title: "test example", + path: [ + { lat: 1, lng: 2 }, + { lat: 2, lng: 3 }, + ], + pathLength: 300, + userId: "June1010", + hDong: { + name: "서울특별시", + }, +}; + +export const expectedRecruitData = { + id: 1, + title: "test example", + startTime: "2037-12-29T15:00:00.000Z", + maxPpl: 1, + currentPpl: 1, + userId: "June1010", + pace: 300, +}; diff --git a/server/test/data/mockData.ts b/server/test/data/mockData.ts new file mode 100644 index 0000000..bcb3188 --- /dev/null +++ b/server/test/data/mockData.ts @@ -0,0 +1,40 @@ +export const mockCourse = (title: string) => { + return { + title: title, + path: [ + { lat: 1, lng: 2 }, + { lat: 2, lng: 3 }, + ], + pathLength: 300, + hCode: "1100000000", + }; +}; + +export const mockRecruit = { + title: "test example", + startTime: new Date(2037, 11, 30).toISOString(), + maxPpl: 5, + pace: 300, + courseId: 1, +}; + +export const mockUserNoEmail = (id: string) => { + return { + userId: id, + password: "1234567890", + pace: 300, + hCode: "1100000000", + email: "catenarywkd@naver.com", + receiveMail: false, + }; +}; + +export const mockAxiosResponse = (statusCode: number) => { + return { + data: {}, + headers: {}, + config: {}, + statusText: "OK", + status: statusCode, + }; +}; diff --git a/server/test/data/requestData.ts b/server/test/data/requestData.ts new file mode 100644 index 0000000..47bea85 --- /dev/null +++ b/server/test/data/requestData.ts @@ -0,0 +1,6 @@ +export const requestRecruitMockData = { + title: "test example", + startTime: new Date(2037, 11, 30).toISOString(), + maxPpl: 1, //ALERT, MAXPPL = 1 + pace: 300, +}; diff --git a/server/test/helpers/users.ts b/server/test/helpers/users.ts new file mode 100644 index 0000000..e69de29 diff --git a/server/test/types/courseResponse.ts b/server/test/types/courseResponse.ts new file mode 100644 index 0000000..e69de29 diff --git a/server/test/types/coursesResponse.ts b/server/test/types/coursesResponse.ts new file mode 100644 index 0000000..e69de29