From e0c7ec1c699dfb4d329b003e9e5295a55ae0cfa5 Mon Sep 17 00:00:00 2001 From: tpgus2603 <kakaneymar2424@gmail.com> Date: Sat, 23 Nov 2024 15:57:18 +0900 Subject: [PATCH] =?UTF-8?q?test/refactor=20:=20=EC=84=9C=EB=B9=84=EC=8A=A4?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=81(#13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dtos/MeetingDetailResponseDTO.js | 22 +++--- dtos/MeetingResponseDTO.js | 18 +++-- middlewares/auth.js | 4 +- models/Friend.js | 9 +-- models/index.js | 6 +- models/meeting.js | 11 +-- models/meetingParticipant.js | 17 +---- models/schedule.js | 2 +- services/meetingService.js | 120 +++++++++++++++---------------- services/meetingService.test.js | 68 +++++++++++++----- 10 files changed, 139 insertions(+), 138 deletions(-) diff --git a/dtos/MeetingDetailResponseDTO.js b/dtos/MeetingDetailResponseDTO.js index 0c489f0..5d4ac75 100644 --- a/dtos/MeetingDetailResponseDTO.js +++ b/dtos/MeetingDetailResponseDTO.js @@ -1,19 +1,21 @@ // dtos/MeetingResponseDTO.js - -class MeetingResponseDTO { - constructor(meeting, isParticipant, isScheduleConflict, creatorName) { +class MeetingDetailResponseDTO { + constructor(meeting) { this.id = meeting.id; this.title = meeting.title; this.description = meeting.description; - this.timeIdxStart = meeting.time_idx_start; // 변경된 필드 - this.timeIdxEnd = meeting.time_idx_end; // 변경된 필드 + this.timeIdxStart = meeting.time_idx_start; + this.timeIdxEnd = meeting.time_idx_end; this.location = meeting.location; - this.deadline = meeting.deadline; + this.time_idx_deadline = meeting.time_idx_deadline; this.type = meeting.type; - this.creatorName = creatorName; - this.isParticipant = isParticipant; - this.isScheduleConflict = isScheduleConflict; + this.creatorName = meeting.creator ? meeting.creator.name : 'Unknown'; + this.participants = meeting.participants.map(participant => ({ + 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 diff --git a/dtos/MeetingResponseDTO.js b/dtos/MeetingResponseDTO.js index f7cc8ef..67c3171 100644 --- a/dtos/MeetingResponseDTO.js +++ b/dtos/MeetingResponseDTO.js @@ -1,22 +1,20 @@ // dtos/MeetingDetailResponseDTO.js -class MeetingDetailResponseDTO { - constructor(meeting) { + +class MeetingResponseDTO { + constructor(meeting, isParticipant, isScheduleConflict, creatorName) { this.id = meeting.id; this.title = meeting.title; this.description = meeting.description; this.timeIdxStart = meeting.time_idx_start; this.timeIdxEnd = meeting.time_idx_end; this.location = meeting.location; - this.deadline = meeting.deadline; + this.time_idx_deadline = meeting.time_idx_deadline; this.type = meeting.type; - this.creatorName = meeting.creator ? meeting.creator.name : 'Unknown'; - this.participants = meeting.participants.map(participant => ({ - userId: participant.user_id, - name: participant.participantUser ? participant.participantUser.name : 'Unknown', - email: participant.participantUser ? participant.participantUser.email : 'Unknown' - })); + this.creatorName = creatorName; + this.isParticipant = isParticipant; + this.isScheduleConflict = isScheduleConflict; } } -module.exports = MeetingDetailResponseDTO; +module.exports = MeetingResponseDTO; \ No newline at end of file diff --git a/middlewares/auth.js b/middlewares/auth.js index 8ae9be0..afc74ea 100644 --- a/middlewares/auth.js +++ b/middlewares/auth.js @@ -1,13 +1,13 @@ // middlewares/auth.js -exports.isLoggedIn = (req, res, next) => { +exports.isLoggedIn = (req, res, next) => { //로그인된 사용자자만 접근허용 if (req.isAuthenticated()) { return next(); } res.redirect('/auth/login'); }; -exports.isNotLoggedIn = (req, res, next) => { +exports.isNotLoggedIn = (req, res, next) => { //로그인 안되면 리다이렉트 if (!req.isAuthenticated()) { return next(); } diff --git a/models/Friend.js b/models/Friend.js index add987c..ed09212 100644 --- a/models/Friend.js +++ b/models/Friend.js @@ -2,7 +2,7 @@ const { DataTypes } = require('sequelize'); const sequelize = require('../config/sequelize'); -const User = require('./User'); +const User = require('./user'); const Friend = sequelize.define('Friend', { status: { @@ -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; diff --git a/models/index.js b/models/index.js index adb731e..a07f247 100644 --- a/models/index.js +++ b/models/index.js @@ -1,12 +1,12 @@ // models/index.js const sequelize = require('../config/sequelize'); -const User = require('./User'); +const User = require('./user'); const Friend = require('./Friend'); const Schedule = require('./Schedule'); const Meeting = require('./Meeting'); const MeetingParticipant = require('./MeetingParticipant'); -const ChatRoom = require('./ChatRooms'); +const ChatRooms = require('./ChatRooms'); // 관계 설정 Friend.belongsTo(User, { foreignKey: 'requester_id', as: 'requester' }); // 친구 요청을 보낸 사용자 @@ -34,5 +34,5 @@ module.exports = { Schedule, Meeting, MeetingParticipant, - ChatRoom, + ChatRooms, }; diff --git a/models/meeting.js b/models/meeting.js index 3d006a5..0616319 100644 --- a/models/meeting.js +++ b/models/meeting.js @@ -1,7 +1,7 @@ // models/Meeting.js const { DataTypes } = require('sequelize'); const sequelize = require('../config/sequelize'); -const User = require('./User'); +const User = require('./user'); const Meeting = sequelize.define('Meeting', { title: { @@ -22,7 +22,7 @@ const Meeting = sequelize.define('Meeting', { location: { type: DataTypes.STRING, }, - deadline: { + time_idx_deadline: { type: DataTypes.INTEGER, }, type: { @@ -34,11 +34,4 @@ const Meeting = sequelize.define('Meeting', { 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; diff --git a/models/meetingParticipant.js b/models/meetingParticipant.js index 288d3b9..59a2624 100644 --- a/models/meetingParticipant.js +++ b/models/meetingParticipant.js @@ -3,28 +3,13 @@ const { DataTypes } = require('sequelize'); const sequelize = require('../config/sequelize'); const Meeting =require('./Meeting'); -const User = require('./User'); +const User = require('./user'); const MeetingParticipant = sequelize.define('MeetingParticipant', { - meeting_id: { - type: DataTypes.INTEGER, - allowNull: false - }, - user_id: { - type: DataTypes.INTEGER, - allowNull: false - } -}, { tableName: 'MeetingParticipants', 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; diff --git a/models/schedule.js b/models/schedule.js index a7c4a42..48dcec4 100644 --- a/models/schedule.js +++ b/models/schedule.js @@ -1,7 +1,7 @@ // models/Schedule.js const { DataTypes } = require('sequelize'); const sequelize = require('../config/sequelize'); -const User = require('./User'); +const User = require('./user'); const Schedule = sequelize.define('Schedule', { title: { diff --git a/services/meetingService.js b/services/meetingService.js index 0d87a0c..3132239 100644 --- a/services/meetingService.js +++ b/services/meetingService.js @@ -122,40 +122,37 @@ class MeetingService { * @returns {Promise<Array<MeetingResponseDTO>>} - 모임 목록 DTO 배열 */ async getMeetings(userId) { - const meetings = await Meeting.findAll({ - attributes: [ - 'id', - 'title', - 'description', - 'time_idx_start', - 'time_idx_end', - 'location', - 'time_idx_deadline', - 'type', - ], - include: [ - { - model: User, - as: 'creator', - attributes: ['name'], - }, - { - model: MeetingParticipant, - as: 'participants', - attributes: ['user_id'], - }, - ], - }); - - return meetings.map((meeting) => { - const creatorName = meeting.creator ? meeting.creator.name : 'Unknown'; - const isParticipant = meeting.participants.some( - (participant) => participant.user_id === parseInt(userId, 10) - ); - - return new MeetingResponseDTO(meeting, isParticipant, false, creatorName); - }); - } + const meetings = await Meeting.findAll({ + attributes: [ + 'id', + 'title', + 'description', + 'time_idx_start', + 'time_idx_end', + 'location', + 'time_idx_deadline', + 'type', + ], + include: [ + { + model: MeetingParticipant, + as: 'participants', + where: { user_id: userId }, // userId와 매핑된 미팅만 가져옴 + attributes: [], // MeetingParticipant 테이블의 데이터는 필요 없으므로 제외 + }, + { + model: User, + as: 'creator', + attributes: ['name'], // 미팅 생성자의 이름만 필요 + }, + ], + }); + + return meetings.map((meeting) => { + const creatorName = meeting.creator ? meeting.creator.name : 'Unknown'; + return new MeetingResponseDTO(meeting, true, false, creatorName); + }); + } /** * 번개 모임 마감 @@ -262,34 +259,35 @@ class MeetingService { * @param {number} meetingId - 모임 ID * @returns {Promise<MeetingDetailResponseDTO>} - 모임 상세 DTO */ - async getMeetingDetail(meetingId) { - const meeting = await Meeting.findByPk(meetingId, { - include: [ - { - model: User, - as: 'creator', - attributes: ['name'], - }, - { - model: MeetingParticipant, - as: 'participants', - include: [ - { - model: User, - as: 'participantUser', - attributes: ['name', 'email'], - }, - ], - }, - ], - }); - - if (!meeting) { - throw new Error('모임을 찾을 수 없습니다.'); - } - - return new MeetingDetailResponseDTO(meeting); + // services/meetingService.js + async getMeetingDetail(meetingId) { + const meeting = await Meeting.findByPk(meetingId, { + include: [ + { + model: User, + as: "creator", + attributes: ["name"], + }, + { + model: MeetingParticipant, + as: "participants", + include: [ + { + model: User, + as: "user", // 'participantUser'에서 'user'로 수정 + attributes: ["name", "email"], + }, + ], + }, + ], + }); + + if (!meeting) { + throw new Error("모임을 찾을 수 없습니다."); } + + return new MeetingDetailResponseDTO(meeting); + } } module.exports = new MeetingService(); diff --git a/services/meetingService.test.js b/services/meetingService.test.js index 6471bba..2bea20b 100644 --- a/services/meetingService.test.js +++ b/services/meetingService.test.js @@ -9,6 +9,14 @@ const CreateMeetingRequestDTO = require('../dtos/CreateMeetingRequestDTO'); const MeetingResponseDTO = require('../dtos/MeetingResponseDTO'); const MeetingDetailResponseDTO = require('../dtos/MeetingDetailResponseDTO'); +// Jest 설정에서 'node' 환경을 사용하고 있는지 확인 +// jest.config.js 또는 package.json에 다음을 추가하세요: +// { +// "jest": { +// "testEnvironment": "node" +// } +// } + // ChatRooms 모듈 전체를 모킹하지 않고, 필요한 메서드만 선택적으로 모킹합니다. beforeAll(async () => { // 테스트 스위트가 시작되기 전에 데이터베이스를 동기화합니다. @@ -129,6 +137,11 @@ describe('MeetingService', () => { 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( 'Schedule overlaps with existing schedule at time_idx 50' ); @@ -160,28 +173,31 @@ describe('MeetingService', () => { created_by: 2, }; - await MeetingService.createMeeting(meetingData1); - await MeetingService.createMeeting(meetingData2); + const result1 = await MeetingService.createMeeting(meetingData1); + const result2 = await MeetingService.createMeeting(meetingData2); const meetings = await MeetingService.getMeetings(1); // Alice의 사용자 ID - + console.log(meetings); expect(meetings).toBeDefined(); expect(Array.isArray(meetings)).toBe(true); - expect(meetings.length).toBe(2); - - meetings.forEach(meeting => { - expect(meeting).toBeInstanceOf(MeetingResponseDTO); - expect(['Meeting 1', 'Meeting 2']).toContain(meeting.title); - expect(['OPEN']).toContain(meeting.type); - if (meeting.id === 1) { - expect(meeting.creatorName).toBe('Alice'); - expect(meeting.isParticipant).toBe(true); - } else { - expect(meeting.creatorName).toBe('Bob'); - expect(meeting.isParticipant).toBe(false); - } - }); + expect(meetings.length).toBe(1); + + // 생성된 미팅의 ID를 기준으로 기대값 설정 + const meeting1 = meetings.find(meeting => meeting.title === 'Meeting 1'); + const meeting2 = meetings.find(meeting => meeting.title === 'Meeting 2'); + + console.log(meeting1); + console.log(meeting2); + expect(meeting1).toBeDefined(); + expect(meeting1.creatorName).toBe('Alice'); + expect(meeting1.isParticipant).toBe(true); + + expect(meeting2).toBeDefined(); + expect(meeting2.creatorName).toBe('Bob'); + expect(meeting2.isParticipant).toBe(false); }); + + // 추가적인 getMeetings 테스트 케이스 작성 가능 }); describe('closeMeeting', () => { @@ -248,6 +264,9 @@ describe('MeetingService', () => { const { meeting_id } = await MeetingService.createMeeting(meetingData); + // 'getCurrentTimeIdx'를 고정된 값으로 모킹하여 '참가 신청이 마감되었습니다.' 오류를 방지 + jest.spyOn(MeetingService, 'getCurrentTimeIdx').mockReturnValue(100); // 100 < 108 + // Bob이 참가 await MeetingService.joinMeeting(meeting_id, 2); @@ -290,6 +309,9 @@ describe('MeetingService', () => { const { meeting_id } = await MeetingService.createMeeting(meetingData); + // 'getCurrentTimeIdx'를 고정된 값으로 모킹 + jest.spyOn(MeetingService, 'getCurrentTimeIdx').mockReturnValue(100); // 100 < 118 + await MeetingService.closeMeeting(meeting_id); await expect(MeetingService.joinMeeting(meeting_id, 2)).rejects.toThrow('이미 마감된 모임입니다.'); @@ -309,6 +331,9 @@ describe('MeetingService', () => { const { meeting_id } = await MeetingService.createMeeting(meetingData); + // 'getCurrentTimeIdx'를 고정된 값으로 모킹 + jest.spyOn(MeetingService, 'getCurrentTimeIdx').mockReturnValue(100); // 100 < 128 + await MeetingService.joinMeeting(meeting_id, 2); await expect(MeetingService.joinMeeting(meeting_id, 2)).rejects.toThrow('이미 참가한 사용자입니다.'); @@ -322,13 +347,19 @@ describe('MeetingService', () => { time_idx_start: 59, time_idx_end: 61, // time_idx 60 포함 location: 'Conference Room H', - time_idx_deadline: 58, + time_idx_deadline: 110, // 참가 신청 마감을 피하기 위해 값 변경 type: 'OPEN', created_by: 1, }; 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( '스케줄이 겹칩니다. 다른 모임에 참가하세요.' ); @@ -351,6 +382,7 @@ describe('MeetingService', () => { const { meeting_id } = await MeetingService.createMeeting(meetingData); // Bob과 Charlie 참가 + jest.spyOn(MeetingService, 'getCurrentTimeIdx').mockReturnValue(100); // 참가 신청 마감 방지 await MeetingService.joinMeeting(meeting_id, 2); await MeetingService.joinMeeting(meeting_id, 3); -- GitLab