Skip to content
Snippets Groups Projects
Commit e0c7ec1c authored by tpgus2603's avatar tpgus2603
Browse files

test/refactor : 서비스코드 리펙토링(#13)

parent c3666785
No related branches found
No related tags found
2 merge requests!31Develop,!23[#13] 스케쥴 서비스 코드 로직 대폭 수정 및 미팅서비스 테스트 완료
// dtos/MeetingResponseDTO.js // dtos/MeetingResponseDTO.js
class MeetingDetailResponseDTO {
class MeetingResponseDTO { constructor(meeting) {
constructor(meeting, isParticipant, isScheduleConflict, creatorName) {
this.id = meeting.id; this.id = meeting.id;
this.title = meeting.title; this.title = meeting.title;
this.description = meeting.description; this.description = meeting.description;
this.timeIdxStart = meeting.time_idx_start; // 변경된 필드 this.timeIdxStart = meeting.time_idx_start;
this.timeIdxEnd = meeting.time_idx_end; // 변경된 필드 this.timeIdxEnd = meeting.time_idx_end;
this.location = meeting.location; this.location = meeting.location;
this.deadline = meeting.deadline; this.time_idx_deadline = meeting.time_idx_deadline;
this.type = meeting.type; this.type = meeting.type;
this.creatorName = creatorName; this.creatorName = meeting.creator ? meeting.creator.name : 'Unknown';
this.isParticipant = isParticipant; this.participants = meeting.participants.map(participant => ({
this.isScheduleConflict = isScheduleConflict; userId: participant.user_id,
name: participant.participantUser ? participant.participantUser.name : 'Unknown',
email: participant.participantUser ? participant.participantUser.email : 'Unknown'
}));
} }
} }
module.exports = MeetingResponseDTO; module.exports = MeetingDetailResponseDTO;
\ No newline at end of file
// dtos/MeetingDetailResponseDTO.js // dtos/MeetingDetailResponseDTO.js
class MeetingDetailResponseDTO {
constructor(meeting) { class MeetingResponseDTO {
constructor(meeting, isParticipant, isScheduleConflict, creatorName) {
this.id = meeting.id; this.id = meeting.id;
this.title = meeting.title; this.title = meeting.title;
this.description = meeting.description; this.description = meeting.description;
this.timeIdxStart = meeting.time_idx_start; this.timeIdxStart = meeting.time_idx_start;
this.timeIdxEnd = meeting.time_idx_end; this.timeIdxEnd = meeting.time_idx_end;
this.location = meeting.location; this.location = meeting.location;
this.deadline = meeting.deadline; this.time_idx_deadline = meeting.time_idx_deadline;
this.type = meeting.type; this.type = meeting.type;
this.creatorName = meeting.creator ? meeting.creator.name : 'Unknown'; this.creatorName = creatorName;
this.participants = meeting.participants.map(participant => ({ this.isParticipant = isParticipant;
userId: participant.user_id, this.isScheduleConflict = isScheduleConflict;
name: participant.participantUser ? participant.participantUser.name : 'Unknown',
email: participant.participantUser ? participant.participantUser.email : 'Unknown'
}));
} }
} }
module.exports = MeetingDetailResponseDTO; module.exports = MeetingResponseDTO;
\ No newline at end of file
// middlewares/auth.js // middlewares/auth.js
exports.isLoggedIn = (req, res, next) => { exports.isLoggedIn = (req, res, next) => { //로그인된 사용자자만 접근허용
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
return next(); return next();
} }
res.redirect('/auth/login'); res.redirect('/auth/login');
}; };
exports.isNotLoggedIn = (req, res, next) => { exports.isNotLoggedIn = (req, res, next) => { //로그인 안되면 리다이렉트
if (!req.isAuthenticated()) { if (!req.isAuthenticated()) {
return next(); return next();
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
const { DataTypes } = require('sequelize'); const { DataTypes } = require('sequelize');
const sequelize = require('../config/sequelize'); const sequelize = require('../config/sequelize');
const User = require('./User'); const User = require('./user');
const Friend = sequelize.define('Friend', { const Friend = sequelize.define('Friend', {
status: { status: {
...@@ -25,11 +25,4 @@ const Friend = sequelize.define('Friend', { ...@@ -25,11 +25,4 @@ const Friend = sequelize.define('Friend', {
] ]
}); });
// // 관계 설정
// Friend.belongsTo(User, { foreignKey: 'requester_id', as: 'requester' }); // 친구 요청을 보낸 사용자
// Friend.belongsTo(User, { foreignKey: 'receiver_id', as: 'receiver' }); // 친구 요청을 받은 사용자
// User.hasMany(Friend, { foreignKey: 'requester_id', as: 'sentRequests' }); // 친구 요청을 보낸 목록
// User.hasMany(Friend, { foreignKey: 'receiver_id', as: 'receivedRequests' }); // 친구 요청을 받은 목록
module.exports = Friend; module.exports = Friend;
// models/index.js // models/index.js
const sequelize = require('../config/sequelize'); const sequelize = require('../config/sequelize');
const User = require('./User'); const User = require('./user');
const Friend = require('./Friend'); const Friend = require('./Friend');
const Schedule = require('./Schedule'); const Schedule = require('./Schedule');
const Meeting = require('./Meeting'); const Meeting = require('./Meeting');
const MeetingParticipant = require('./MeetingParticipant'); const MeetingParticipant = require('./MeetingParticipant');
const ChatRoom = require('./ChatRooms'); const ChatRooms = require('./ChatRooms');
// 관계 설정 // 관계 설정
Friend.belongsTo(User, { foreignKey: 'requester_id', as: 'requester' }); // 친구 요청을 보낸 사용자 Friend.belongsTo(User, { foreignKey: 'requester_id', as: 'requester' }); // 친구 요청을 보낸 사용자
...@@ -34,5 +34,5 @@ module.exports = { ...@@ -34,5 +34,5 @@ module.exports = {
Schedule, Schedule,
Meeting, Meeting,
MeetingParticipant, MeetingParticipant,
ChatRoom, ChatRooms,
}; };
// models/Meeting.js // models/Meeting.js
const { DataTypes } = require('sequelize'); const { DataTypes } = require('sequelize');
const sequelize = require('../config/sequelize'); const sequelize = require('../config/sequelize');
const User = require('./User'); const User = require('./user');
const Meeting = sequelize.define('Meeting', { const Meeting = sequelize.define('Meeting', {
title: { title: {
...@@ -22,7 +22,7 @@ const Meeting = sequelize.define('Meeting', { ...@@ -22,7 +22,7 @@ const Meeting = sequelize.define('Meeting', {
location: { location: {
type: DataTypes.STRING, type: DataTypes.STRING,
}, },
deadline: { time_idx_deadline: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
}, },
type: { type: {
...@@ -34,11 +34,4 @@ const Meeting = sequelize.define('Meeting', { ...@@ -34,11 +34,4 @@ const Meeting = sequelize.define('Meeting', {
timestamps: false, timestamps: false,
}); });
// // 연관 관계 설정
// Meeting.belongsTo(User, { foreignKey: 'created_by', as: 'creator' });
// User.hasMany(Meeting, { foreignKey: 'created_by', as: 'meetings' });
// Meeting.belongsTo(ChatRoom, { foreignKey: 'chatRoomId', as: 'chatRoom' });
// ChatRoom.hasOne(Meeting, { foreignKey: 'chatRoomId', as: 'meeting' });
module.exports = Meeting; module.exports = Meeting;
...@@ -3,28 +3,13 @@ ...@@ -3,28 +3,13 @@
const { DataTypes } = require('sequelize'); const { DataTypes } = require('sequelize');
const sequelize = require('../config/sequelize'); const sequelize = require('../config/sequelize');
const Meeting =require('./Meeting'); const Meeting =require('./Meeting');
const User = require('./User'); const User = require('./user');
const MeetingParticipant = sequelize.define('MeetingParticipant', { const MeetingParticipant = sequelize.define('MeetingParticipant', {
meeting_id: {
type: DataTypes.INTEGER,
allowNull: false
},
user_id: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
tableName: 'MeetingParticipants', tableName: 'MeetingParticipants',
timestamps: false, timestamps: false,
}); });
// MeetingParticipant.belongsTo(Meeting, { foreignKey: 'meeting_id', as: 'meeting' });
// Meeting.hasMany(MeetingParticipant, { foreignKey: 'meeting_id', as: 'participants' });
// MeetingParticipant.belongsTo(User, { foreignKey: 'user_id', as: 'user' });
// User.hasMany(MeetingParticipant, { foreignKey: 'user_id', as: 'meetingParticipations' });
module.exports = MeetingParticipant; module.exports = MeetingParticipant;
// models/Schedule.js // models/Schedule.js
const { DataTypes } = require('sequelize'); const { DataTypes } = require('sequelize');
const sequelize = require('../config/sequelize'); const sequelize = require('../config/sequelize');
const User = require('./User'); const User = require('./user');
const Schedule = sequelize.define('Schedule', { const Schedule = sequelize.define('Schedule', {
title: { title: {
......
...@@ -134,26 +134,23 @@ class MeetingService { ...@@ -134,26 +134,23 @@ class MeetingService {
'type', 'type',
], ],
include: [ include: [
{
model: User,
as: 'creator',
attributes: ['name'],
},
{ {
model: MeetingParticipant, model: MeetingParticipant,
as: 'participants', as: 'participants',
attributes: ['user_id'], where: { user_id: userId }, // userId와 매핑된 미팅만 가져옴
attributes: [], // MeetingParticipant 테이블의 데이터는 필요 없으므로 제외
},
{
model: User,
as: 'creator',
attributes: ['name'], // 미팅 생성자의 이름만 필요
}, },
], ],
}); });
return meetings.map((meeting) => { return meetings.map((meeting) => {
const creatorName = meeting.creator ? meeting.creator.name : 'Unknown'; const creatorName = meeting.creator ? meeting.creator.name : 'Unknown';
const isParticipant = meeting.participants.some( return new MeetingResponseDTO(meeting, true, false, creatorName);
(participant) => participant.user_id === parseInt(userId, 10)
);
return new MeetingResponseDTO(meeting, isParticipant, false, creatorName);
}); });
} }
...@@ -262,22 +259,23 @@ class MeetingService { ...@@ -262,22 +259,23 @@ class MeetingService {
* @param {number} meetingId - 모임 ID * @param {number} meetingId - 모임 ID
* @returns {Promise<MeetingDetailResponseDTO>} - 모임 상세 DTO * @returns {Promise<MeetingDetailResponseDTO>} - 모임 상세 DTO
*/ */
// services/meetingService.js
async getMeetingDetail(meetingId) { async getMeetingDetail(meetingId) {
const meeting = await Meeting.findByPk(meetingId, { const meeting = await Meeting.findByPk(meetingId, {
include: [ include: [
{ {
model: User, model: User,
as: 'creator', as: "creator",
attributes: ['name'], attributes: ["name"],
}, },
{ {
model: MeetingParticipant, model: MeetingParticipant,
as: 'participants', as: "participants",
include: [ include: [
{ {
model: User, model: User,
as: 'participantUser', as: "user", // 'participantUser'에서 'user'로 수정
attributes: ['name', 'email'], attributes: ["name", "email"],
}, },
], ],
}, },
...@@ -285,7 +283,7 @@ class MeetingService { ...@@ -285,7 +283,7 @@ class MeetingService {
}); });
if (!meeting) { if (!meeting) {
throw new Error('모임을 찾을 수 없습니다.'); throw new Error("모임을 찾을 수 없습니다.");
} }
return new MeetingDetailResponseDTO(meeting); return new MeetingDetailResponseDTO(meeting);
......
...@@ -9,6 +9,14 @@ const CreateMeetingRequestDTO = require('../dtos/CreateMeetingRequestDTO'); ...@@ -9,6 +9,14 @@ const CreateMeetingRequestDTO = require('../dtos/CreateMeetingRequestDTO');
const MeetingResponseDTO = require('../dtos/MeetingResponseDTO'); const MeetingResponseDTO = require('../dtos/MeetingResponseDTO');
const MeetingDetailResponseDTO = require('../dtos/MeetingDetailResponseDTO'); const MeetingDetailResponseDTO = require('../dtos/MeetingDetailResponseDTO');
// Jest 설정에서 'node' 환경을 사용하고 있는지 확인
// jest.config.js 또는 package.json에 다음을 추가하세요:
// {
// "jest": {
// "testEnvironment": "node"
// }
// }
// ChatRooms 모듈 전체를 모킹하지 않고, 필요한 메서드만 선택적으로 모킹합니다. // ChatRooms 모듈 전체를 모킹하지 않고, 필요한 메서드만 선택적으로 모킹합니다.
beforeAll(async () => { beforeAll(async () => {
// 테스트 스위트가 시작되기 전에 데이터베이스를 동기화합니다. // 테스트 스위트가 시작되기 전에 데이터베이스를 동기화합니다.
...@@ -129,6 +137,11 @@ describe('MeetingService', () => { ...@@ -129,6 +137,11 @@ describe('MeetingService', () => {
created_by: 1, created_by: 1,
}; };
// 'createSchedules' 메서드에서 'Schedule overlaps with existing schedule at time_idx 50' 오류가 발생해야 함
// 이를 위해 'ScheduleService.checkScheduleOverlapByTime'을 모킹합니다.
jest.spyOn(ScheduleService, 'checkScheduleOverlapByTime').mockResolvedValue(true); // 충돌이 발생한다고 가정
await expect(MeetingService.createMeeting(meetingData)).rejects.toThrow( await expect(MeetingService.createMeeting(meetingData)).rejects.toThrow(
'Schedule overlaps with existing schedule at time_idx 50' 'Schedule overlaps with existing schedule at time_idx 50'
); );
...@@ -160,28 +173,31 @@ describe('MeetingService', () => { ...@@ -160,28 +173,31 @@ describe('MeetingService', () => {
created_by: 2, created_by: 2,
}; };
await MeetingService.createMeeting(meetingData1); const result1 = await MeetingService.createMeeting(meetingData1);
await MeetingService.createMeeting(meetingData2); const result2 = await MeetingService.createMeeting(meetingData2);
const meetings = await MeetingService.getMeetings(1); // Alice의 사용자 ID const meetings = await MeetingService.getMeetings(1); // Alice의 사용자 ID
console.log(meetings);
expect(meetings).toBeDefined(); expect(meetings).toBeDefined();
expect(Array.isArray(meetings)).toBe(true); expect(Array.isArray(meetings)).toBe(true);
expect(meetings.length).toBe(2); expect(meetings.length).toBe(1);
meetings.forEach(meeting => { // 생성된 미팅의 ID를 기준으로 기대값 설정
expect(meeting).toBeInstanceOf(MeetingResponseDTO); const meeting1 = meetings.find(meeting => meeting.title === 'Meeting 1');
expect(['Meeting 1', 'Meeting 2']).toContain(meeting.title); const meeting2 = meetings.find(meeting => meeting.title === 'Meeting 2');
expect(['OPEN']).toContain(meeting.type);
if (meeting.id === 1) { console.log(meeting1);
expect(meeting.creatorName).toBe('Alice'); console.log(meeting2);
expect(meeting.isParticipant).toBe(true); expect(meeting1).toBeDefined();
} else { expect(meeting1.creatorName).toBe('Alice');
expect(meeting.creatorName).toBe('Bob'); expect(meeting1.isParticipant).toBe(true);
expect(meeting.isParticipant).toBe(false);
} expect(meeting2).toBeDefined();
}); expect(meeting2.creatorName).toBe('Bob');
expect(meeting2.isParticipant).toBe(false);
}); });
// 추가적인 getMeetings 테스트 케이스 작성 가능
}); });
describe('closeMeeting', () => { describe('closeMeeting', () => {
...@@ -248,6 +264,9 @@ describe('MeetingService', () => { ...@@ -248,6 +264,9 @@ describe('MeetingService', () => {
const { meeting_id } = await MeetingService.createMeeting(meetingData); const { meeting_id } = await MeetingService.createMeeting(meetingData);
// 'getCurrentTimeIdx'를 고정된 값으로 모킹하여 '참가 신청이 마감되었습니다.' 오류를 방지
jest.spyOn(MeetingService, 'getCurrentTimeIdx').mockReturnValue(100); // 100 < 108
// Bob이 참가 // Bob이 참가
await MeetingService.joinMeeting(meeting_id, 2); await MeetingService.joinMeeting(meeting_id, 2);
...@@ -290,6 +309,9 @@ describe('MeetingService', () => { ...@@ -290,6 +309,9 @@ describe('MeetingService', () => {
const { meeting_id } = await MeetingService.createMeeting(meetingData); const { meeting_id } = await MeetingService.createMeeting(meetingData);
// 'getCurrentTimeIdx'를 고정된 값으로 모킹
jest.spyOn(MeetingService, 'getCurrentTimeIdx').mockReturnValue(100); // 100 < 118
await MeetingService.closeMeeting(meeting_id); await MeetingService.closeMeeting(meeting_id);
await expect(MeetingService.joinMeeting(meeting_id, 2)).rejects.toThrow('이미 마감된 모임입니다.'); await expect(MeetingService.joinMeeting(meeting_id, 2)).rejects.toThrow('이미 마감된 모임입니다.');
...@@ -309,6 +331,9 @@ describe('MeetingService', () => { ...@@ -309,6 +331,9 @@ describe('MeetingService', () => {
const { meeting_id } = await MeetingService.createMeeting(meetingData); const { meeting_id } = await MeetingService.createMeeting(meetingData);
// 'getCurrentTimeIdx'를 고정된 값으로 모킹
jest.spyOn(MeetingService, 'getCurrentTimeIdx').mockReturnValue(100); // 100 < 128
await MeetingService.joinMeeting(meeting_id, 2); await MeetingService.joinMeeting(meeting_id, 2);
await expect(MeetingService.joinMeeting(meeting_id, 2)).rejects.toThrow('이미 참가한 사용자입니다.'); await expect(MeetingService.joinMeeting(meeting_id, 2)).rejects.toThrow('이미 참가한 사용자입니다.');
...@@ -322,13 +347,19 @@ describe('MeetingService', () => { ...@@ -322,13 +347,19 @@ describe('MeetingService', () => {
time_idx_start: 59, time_idx_start: 59,
time_idx_end: 61, // time_idx 60 포함 time_idx_end: 61, // time_idx 60 포함
location: 'Conference Room H', location: 'Conference Room H',
time_idx_deadline: 58, time_idx_deadline: 110, // 참가 신청 마감을 피하기 위해 값 변경
type: 'OPEN', type: 'OPEN',
created_by: 1, created_by: 1,
}; };
const { meeting_id } = await MeetingService.createMeeting(meetingData); const { meeting_id } = await MeetingService.createMeeting(meetingData);
// 'getCurrentTimeIdx'를 고정된 값으로 모킹
jest.spyOn(MeetingService, 'getCurrentTimeIdx').mockReturnValue(100); // 100 < 110
// 'checkScheduleOverlapByTime'을 모킹하여 충돌이 발생하도록 설정
jest.spyOn(ScheduleService, 'checkScheduleOverlapByTime').mockResolvedValue(true);
await expect(MeetingService.joinMeeting(meeting_id, 2)).rejects.toThrow( await expect(MeetingService.joinMeeting(meeting_id, 2)).rejects.toThrow(
'스케줄이 겹칩니다. 다른 모임에 참가하세요.' '스케줄이 겹칩니다. 다른 모임에 참가하세요.'
); );
...@@ -351,6 +382,7 @@ describe('MeetingService', () => { ...@@ -351,6 +382,7 @@ describe('MeetingService', () => {
const { meeting_id } = await MeetingService.createMeeting(meetingData); const { meeting_id } = await MeetingService.createMeeting(meetingData);
// Bob과 Charlie 참가 // Bob과 Charlie 참가
jest.spyOn(MeetingService, 'getCurrentTimeIdx').mockReturnValue(100); // 참가 신청 마감 방지
await MeetingService.joinMeeting(meeting_id, 2); await MeetingService.joinMeeting(meeting_id, 2);
await MeetingService.joinMeeting(meeting_id, 3); await MeetingService.joinMeeting(meeting_id, 3);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment