Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Refactor #204: mock repository와 controller로 분리하기 #205

Merged
merged 17 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
8d0bdbf
feat(#191): playwright 설치하기
naarang Dec 2, 2024
b966ccf
Merge branch 'main' of https://github.com/boostcampwm-2024/web38-Frox…
naarang Dec 2, 2024
559a5f3
feat(#191): playwright로 간단히 테스트해보기
naarang Dec 3, 2024
b1e238f
refactor(#191): mock 변경 사항 반영 및 불필요한 resolver 제거 및 resolver 함수명 통일하기
naarang Dec 3, 2024
81dc669
refactor(#191): mock history 불필요한 함수 제거 및 DTO 적용하기
naarang Dec 3, 2024
816b8a1
refactor(#191): mock lotus 제목 검색 로직 추가하기
naarang Dec 3, 2024
055b1bb
Merge branch 'main' of https://github.com/boostcampwm-2024/web38-Frox…
naarang Dec 3, 2024
231eaa5
refactor(#191): mocking 시에 페이지네이션 max 값도 계산하기
naarang Dec 3, 2024
d3d3098
refactor(#191): user에 대한 MockRepository 적용하기
naarang Dec 3, 2024
dd97ad1
refactor(#191): 테스트를 위한 data-testid값을 부여
naarang Dec 3, 2024
d178a4e
fix(#191): lotus 삭제 시 오류 수정하기
naarang Dec 3, 2024
ca8d710
refactor(#191): app 레이어로 테스트 코드 이동 및 e2e 테스트 코드 작성완료
naarang Dec 3, 2024
1268e09
refactor(#204): mock 코드를 controller와 repository로 분리하기
naarang Dec 4, 2024
baebb61
refactor(#204): lotus 생성 mocking 시에 유저 레포지토리로 유저 데이터 관리하도록 변경하기
naarang Dec 5, 2024
2c4e4bb
refactor(#204): mocking 시에 findLotusDetail을 repository에서 controller로 …
naarang Dec 5, 2024
fffa2f1
Merge branch 'main' of https://github.com/boostcampwm-2024/web38-Frox…
naarang Dec 5, 2024
aef1f6a
fix(#204): fe ci 오류 해결하기
naarang Dec 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 2 additions & 25 deletions apps/frontend/src/app/mock/MockRepository/MockRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export type Identifiable<T> = T & { id: string };

export class MockRepository<T> {
private _autoId = 0;
private memory: Identifiable<T>[] = [];
protected memory: Identifiable<T>[] = [];

private isMatch(owner: Partial<T>, target: Partial<T>): boolean {
for (const key in target) {
Expand All @@ -16,29 +16,11 @@ export class MockRepository<T> {
return true;
}

private isPartialMatch(owner: Partial<T>, target: Partial<T>): boolean {
for (const key in target) {
if (!Object.prototype.hasOwnProperty.call(owner, key)) return false;

const ownerValue = owner[key as keyof T];
const targetValue = target[key as keyof T];

if (typeof ownerValue === 'boolean' && ownerValue !== targetValue) {
return false;
}

if (typeof targetValue === 'string' && !(ownerValue as string)?.includes(targetValue)) {
return false;
}
}
return true;
}

private generateId() {
return String(this._autoId++);
}

private paginate(items: Identifiable<T>[], page: number, size: number) {
protected paginate(items: Identifiable<T>[], page: number, size: number) {
const start = (page - 1) * size;
const end = start + size;
const data = items.slice(start, end);
Expand Down Expand Up @@ -88,9 +70,4 @@ export class MockRepository<T> {

return data;
}

async search({ query, page = 1, size = 10 }: { query?: Partial<Identifiable<T>>; page?: number; size?: number }) {
const filtered = query ? this.memory.filter((item) => this.isPartialMatch(item, query)) : this.memory;
return this.paginate(filtered, page, size);
}
}
93 changes: 93 additions & 0 deletions apps/frontend/src/app/mock/controller/historyController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { DefaultBodyType, HttpResponse, PathParams, StrictRequest } from 'msw';
import { historyRepository } from '@/app/mock/repository/historyRepository';

// Lotus History 목록 조회
export const getHistoryList = async ({
request,
params
}: {
request: StrictRequest<DefaultBodyType>;
params: PathParams;
}) => {
const { lotusId } = params;

const url = new URL(request.url);
const page = Number(url.searchParams.get('page')) || 1;

if (!lotusId) {
return new HttpResponse('Bad Request', {
status: 400,
headers: {
'Content-Type': 'text/plain'
}
});
}
const { data, maxPage: max } = await historyRepository.findMany({ page });
const list = data.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());

return HttpResponse.json({
list,
page: {
current: page,
max
}
});
};

// 코드 실행
interface PostCodeRunBody {
input?: string;
execFileName: string;
}

export const postCodeRun = async ({ request }: { request: StrictRequest<DefaultBodyType> }) => {
const body = (await request.json()) as PostCodeRunBody;

if (!body?.execFileName)
return new HttpResponse('Bad Request', {
status: 400,
headers: {
'Content-Type': 'text/plain'
}
});

const newHistory = await historyRepository.create({
filename: body.execFileName,
date: new Date().toISOString(),
status: 'PENDING',
input: body?.input ?? '',
output: ''
});

setTimeout(() => {
historyRepository.update(
{ id: newHistory.id },
{
status: 'SUCCESS',
output: `입력한 값: ${newHistory.input} `
}
);
}, 2000);

return HttpResponse.json({
status: newHistory.status
});
};

// 해당 히스토리 정보
export const getHistory = async ({ params }: { params: PathParams }) => {
const { lotusId, historyId } = params;

if (!lotusId || !historyId) {
return new HttpResponse('Bad Request', {
status: 400,
headers: {
'Content-Type': 'text/plain'
}
});
}

const history = await historyRepository.findOne({ id: historyId as string });

return HttpResponse.json(history);
};
129 changes: 129 additions & 0 deletions apps/frontend/src/app/mock/controller/lotusController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { DefaultBodyType, HttpResponse, PathParams, StrictRequest } from 'msw';
import { lotusMockFileData, lotusRepository } from '@/app/mock/repository/lotusRepository';
import { userRepository } from '@/app/mock/repository/userRepository';
import { LotusDto } from '@/feature/lotus';

const MOCK_UUID = 'mock-uuid';

// 사용자의 Lotus 목록 조회
export const getUserLotusList = async ({ request }: { request: StrictRequest<DefaultBodyType> }) => {
const authorization = request.headers.get('Authorization');

const [type, token] = authorization?.split(' ') || [];

if (token !== MOCK_UUID || type !== 'Bearer') {
return new HttpResponse('Unauthorized: Invalid or missing token', {
status: 401,
headers: {
'Content-Type': 'text/plain'
}
});
}

const url = new URL(request.url);
const page = Number(url.searchParams.get('page')) || 1;
const size = Number(url.searchParams.get('size')) || 5;

const { data: lotuses, maxPage: max } = await lotusRepository.findMany({ page, size });

return HttpResponse.json({
lotuses,
page: {
current: page,
max
}
});
};

// public lotus 목록 조회
export const getPublicLotusList = async ({ request }: { request: StrictRequest<DefaultBodyType> }) => {
const url = new URL(request.url);
const page = Number(url.searchParams.get('page')) || 1;
const size = Number(url.searchParams.get('size')) || 5;
const search = url.searchParams.get('search') || '';

const { data: lotuses, maxPage: max } = await lotusRepository.search({
query: { title: search, isPublic: true },
page,
size
});

return HttpResponse.json({
lotuses,
page: {
current: page,
max
}
});
};

// public lotus 상세 조회
export const getLotusDetail = async ({ params }: { params: Record<string, string> }) => {
const lotusId = params.lotusId;

const lotus = await lotusRepository.findOne({ id: lotusId });

return HttpResponse.json({ ...lotus, ...lotusMockFileData });
};

type CreateLotusDto = {
title: string;
isPublic: false;
tags: string[];
gistUuid: string;
};

//lotus 생성
export const postCreateLotus = async ({ request }: { request: StrictRequest<DefaultBodyType> }) => {
const body = (await request.json()) as CreateLotusDto;

const author = await userRepository.findOne({ id: '0' });

const lotus = await lotusRepository.create({
...body,
date: new Date().toISOString(),
author,
logo: '/image/exampleImage.jpeg',
link: 'https://devblog.com/articles/1000000001',
isPublic: false,
gistUrl: ''
});

return HttpResponse.json(lotus);
};

// lotus 수정
export const patchLotus = async ({
params,
request
}: {
params: PathParams;
request: StrictRequest<DefaultBodyType>;
}) => {
const { id } = params;

const body = (await request.json()) as Partial<LotusDto>;

if (!id || typeof id !== 'string') return HttpResponse.json({ message: 'id is required' });

const lotus = await lotusRepository.findOne({ id });

const updatedLotus = await lotusRepository.update(lotus, body);

return HttpResponse.json(updatedLotus);
};

// lotus 삭제
export const deleteLotus = async ({ params }: { params: PathParams }) => {
const { id } = params;

if (!id || typeof id !== 'string') return HttpResponse.json({ message: 'id is required' });

const lotus = await lotusRepository.findOne({
id
});

await lotusRepository.delete(lotus);

return HttpResponse.json(lotus);
};
Original file line number Diff line number Diff line change
@@ -1,25 +1,7 @@
import { DefaultBodyType, HttpResponse, StrictRequest } from 'msw';
import { MockRepository } from './MockRepository';
import { MOCK_CODE, MOCK_UUID, userRepository } from '@/app/mock/repository/userRepository';
import { UserDto } from '@/feature/user';

const MOCK_CODE = 'mock-code';
const MOCK_UUID = 'mock-uuid';

const userList = new MockRepository<Omit<UserDto, 'id'>>();

const insertUser = () => {
const userMock: UserDto = {
id: '1',
nickname: 'mockUser',
profile: '/image/exampleImage.jpeg',
gistUrl: 'https://github.com'
};

userList.create(userMock);
};

insertUser();

// github 사용자 기본 정보 조회 api
export const getUserInfo = async ({ request }: { request: StrictRequest<DefaultBodyType> }) => {
const authorization = request.headers.get('Authorization');
Expand All @@ -35,13 +17,12 @@ export const getUserInfo = async ({ request }: { request: StrictRequest<DefaultB
});
}

const user = await userList.findOne({ id: '0' });
const user = await userRepository.findOne({ id: '0' });

return HttpResponse.json(user);
};

// 사용자 프로필 수정 api - 일단 닉네임만 수정되도록

export const patchUserInfo = async ({ request }: { request: StrictRequest<DefaultBodyType> }) => {
const authorization = request.headers.get('Authorization');

Expand All @@ -57,9 +38,9 @@ export const patchUserInfo = async ({ request }: { request: StrictRequest<Defaul
try {
const body = (await request.json()) as Partial<UserDto>;

const user = await userList.findOne({ id: '0' });
const user = await userRepository.findOne({ id: '0' });

const updatedUser = await userList.update(user, body);
const updatedUser = await userRepository.update(user, body);

return HttpResponse.json(updatedUser);
} catch (error) {
Expand Down
19 changes: 11 additions & 8 deletions apps/frontend/src/app/mock/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { http } from 'msw';
import { getGistDetail, getUserGistList } from './gistResolvers';
import { getHistory, getHistoryList, getTagList, postCodeRun, postTag } from './historyResolvers';
import { getGistDetail, getUserGistList } from './controller/gistController';
import { getHistory, getHistoryList, postCodeRun } from './controller/historyController';
import {
deleteLotus,
getLotusDetail,
getPublicLotusList,
getUserLotusList,
patchLotus,
postCreateLotus
} from './lotusResolvers';
import { getLogin, getUserInfo, patchUserInfo } from './userResolvers';
} from './controller/lotusController';
import { getLogin, getUserInfo, patchUserInfo } from './controller/userController';

const apiUrl = (path: string) => `${import.meta.env.VITE_API_URL}${path}`;

Expand All @@ -19,6 +19,7 @@ export const handlers = [
http.patch(apiUrl(`/api/user`), patchUserInfo),
http.get(apiUrl(`/api/user/login/callback`), getLogin),
http.get(apiUrl(`/api/user/lotus`), getUserLotusList),
// gist
http.get(apiUrl(`/api/user/gist`), getUserGistList),
http.get(apiUrl(`/api/user/gist/:gistId`), getGistDetail),
// lotus
Expand All @@ -27,11 +28,13 @@ export const handlers = [
http.post(apiUrl(`/api/lotus`), postCreateLotus),
http.patch(apiUrl(`/api/lotus/:id`), patchLotus),
http.delete(apiUrl(`/api/lotus/:id`), deleteLotus),
http.get(apiUrl(`/api/lotus`), getPublicLotusList),
http.get(apiUrl(`/api/lotus/:lotusId`), getLotusDetail),
http.post(apiUrl(`/api/lotus`), postCreateLotus),
http.patch(apiUrl(`/api/lotus/:id`), patchLotus),
http.delete(apiUrl(`/api/lotus/:id`), deleteLotus),
// history
http.get(apiUrl(`/api/lotus/:lotusId/history`), getHistoryList),
http.post(apiUrl(`/api/lotus/:lotusId/history`), postCodeRun),
http.get(apiUrl(`/api/lotus/:lotusId/history/:historyId`), getHistory),
// tag
http.get(apiUrl(`/api/tag`), getTagList),
http.post(apiUrl(`/api/tag`), postTag)
http.get(apiUrl(`/api/lotus/:lotusId/history/:historyId`), getHistory)
];
Loading
Loading