Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
// test/meetingService.test.js
const { sequelize, User, Friend, Schedule, Meeting, MeetingParticipant, ChatRoom } = require('../models'); // models/index.js를 통해 임포트
const MeetingService = require('../services/meetingService');
const ScheduleService = require('../services/scheduleService'); // ScheduleService 임포트
const chatController = require('../controllers/chatController');
// Jest를 사용하여 chatController 모킹
jest.mock('../controllers/chatController', () => ({
createChatRoomInternal: jest.fn()
}));
describe('MeetingService', () => {
beforeAll(async () => {
await sequelize.sync({ force: true });
// 더미 사용자 생성
await User.bulkCreate([
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' },
]);
// 더미 친구 관계 생성
await Friend.create({
id: 1,
requester_id: 1,
receiver_id: 2,
status: 'ACCEPTED',
});
// 더미 스케줄 생성
await Schedule.bulkCreate([
{
id: 1,
user_id: 1,
title: "Alice's Fixed Schedule",
start_time: new Date('2024-05-01T09:00:00Z'),
end_time: new Date('2024-05-01T10:00:00Z'),
is_fixed: true,
expiry_date: null,
},
{
id: 2,
user_id: 1,
title: "Alice's Flexible Schedule",
start_time: new Date('2024-05-02T11:00:00Z'),
end_time: new Date('2024-05-02T12:00:00Z'),
is_fixed: false,
expiry_date: new Date('2024-05-08T00:00:00Z'), // 다음 월요일
},
]);
// 기본적인 채팅방 생성 모킹 설정 (성공)
chatController.createChatRoomInternal.mockResolvedValue({
success: true,
chatRoomId: 'chatroom-1234'
});
});
afterAll(async () => {
// 데이터베이스 연결 종료
await sequelize.close();
});
beforeEach(() => {
// 각 테스트 전에 mock 호출 이력 초기화
jest.clearAllMocks();
});
describe('createMeeting', () => {
test('번개 모임을 성공적으로 생성해야 한다', async () => {
const meetingData = {
title: 'Tech Talk',
description: 'A discussion on the latest tech trends.',
start_time: '2024-05-10T10:00:00Z',
end_time: '2024-05-10T12:00:00Z',
location: 'Online',
deadline: '2024-05-09T23:59:59Z',
type: 'OPEN',
created_by: 1,
};
const result = await MeetingService.createMeeting(meetingData);
expect(result).toHaveProperty('meeting_id');
expect(result).toHaveProperty('chatRoomId');
expect(result.chatRoomId).toBe('chatroom-1234');
// 모임이 DB에 생성되었는지 확인
const meeting = await Meeting.findByPk(result.meeting_id);
expect(meeting).toBeDefined();
expect(meeting.title).toBe('Tech Talk');
// 참가자가 추가되었는지 확인
const participant = await MeetingParticipant.findOne({
where: { meeting_id: result.meeting_id, user_id: 1 }
});
expect(participant).toBeDefined();
// 스케줄이 추가되었는지 확인
const schedule = await Schedule.findOne({
where: { user_id: 1, title: '번개 모임: Tech Talk' }
});
expect(schedule).toBeDefined();
expect(schedule.start_time.toISOString()).toBe('2024-05-10T10:00:00.000Z');
expect(schedule.end_time.toISOString()).toBe('2024-05-10T12:00:00.000Z');
expect(schedule.is_fixed).toBe(true);
expect(schedule.expiry_date).toBeNull();
// chatController.createChatRoomInternal이 호출되었는지 확인
expect(chatController.createChatRoomInternal).toHaveBeenCalledTimes(1);
expect(chatController.createChatRoomInternal).toHaveBeenCalledWith({
participants: ['Alice']
});
});
test('사용자의 스케줄이 겹치는 경우 모임 생성을 실패해야 한다', async () => {
// Alice의 기존 스케줄 생성 (이미 beforeAll에서 생성됨)
const overlappingMeetingData = {
title: 'Overlap Meeting',
description: 'This meeting overlaps with Alice\'s fixed schedule.',
start_time: '2024-05-01T09:30:00Z', // Alice's Fixed Schedule과 겹침
end_time: '2024-05-01T11:00:00Z',
location: 'Office',
deadline: '2024-04-30T23:59:59Z',
type: 'OPEN',
created_by: 1,
};
await expect(MeetingService.createMeeting(overlappingMeetingData))
.rejects
.toThrow('스케줄이 겹칩니다. 다른 시간을 선택해주세요.');
});
test('모임 생성 시 스케줄의 유동 스케줄이 만료되면 스케줄 충돌이 발생하지 않아야 한다', async () => {
const meetingData = {
title: 'Morning Meeting',
description: 'Meeting after flexible schedule expiry.',
start_time: '2024-05-09T09:00:00Z', // Flexible Schedule의 expiry_date가 지난 시점
end_time: '2024-05-09T10:00:00Z',
location: 'Conference Room',
deadline: '2024-05-08T23:59:59Z',
type: 'OPEN',
created_by: 1,
};
const result = await MeetingService.createMeeting(meetingData);
expect(result).toHaveProperty('meeting_id');
expect(result).toHaveProperty('chatRoomId');
expect(result.chatRoomId).toBe('chatroom-1234');
// 스케줄이 추가되었는지 확인
const schedule = await Schedule.findOne({
where: { user_id: 1, title: '번개 모임: Morning Meeting' }
});
expect(schedule).toBeDefined();
expect(schedule.start_time.toISOString()).toBe('2024-05-09T09:00:00.000Z');
expect(schedule.end_time.toISOString()).toBe('2024-05-09T10:00:00.000Z');
expect(schedule.is_fixed).toBe(true);
expect(schedule.expiry_date).toBeNull();
});
test('모임 생성 시 채팅방 생성 실패하면 에러를 던져야 한다', async () => {
// chatController.createChatRoomInternal을 실패하도록 모킹
chatController.createChatRoomInternal.mockResolvedValueOnce({
success: false
});
const meetingData = {
title: 'Failed ChatRoom Meeting',
description: 'This meeting will fail to create chat room.',
start_time: '2024-05-11T10:00:00Z',
end_time: '2024-05-11T12:00:00Z',
location: 'Online',
deadline: '2024-05-10T23:59:59Z',
type: 'OPEN',
created_by: 1,
};
await expect(MeetingService.createMeeting(meetingData))
.rejects
.toThrow('채팅방 생성 실패');
});
test('모임 생성 시 유효하지 않은 데이터는 검증 에러를 던져야 한다', async () => {
const invalidMeetingData = {
title: '', // 빈 제목
start_time: 'invalid-date',
end_time: '2024-05-10T09:00:00Z', // start_time보다 이전
type: 'INVALID_TYPE',
created_by: -1, // 음수 ID
};
await expect(MeetingService.createMeeting(invalidMeetingData))
.rejects
.toThrow('Validation error');
});
});
describe('joinMeeting', () => {
test('번개 모임에 성공적으로 참가해야 한다', async () => {
// Alice가 모임 생성
const meetingData = {
title: 'Networking Event',
description: 'An event to network with professionals.',
start_time: '2024-06-15T10:00:00Z',
end_time: '2024-06-15T12:00:00Z',
location: 'Conference Hall',
deadline: '2024-06-14T23:59:59Z',
type: 'OPEN',
created_by: 1,
};
const { meeting_id } = await MeetingService.createMeeting(meetingData);
// Bob가 모임에 참가
await MeetingService.joinMeeting(meeting_id, 2);
// 참가자가 추가되었는지 확인
const participant = await MeetingParticipant.findOne({
where: { meeting_id: meeting_id, user_id: 2 }
});
expect(participant).toBeDefined();
// 스케줄이 추가되었는지 확인
const schedule = await Schedule.findOne({
where: { user_id: 2, title: '번개 모임: Networking Event' }
});
expect(schedule).toBeDefined();
expect(schedule.start_time.toISOString()).toBe('2024-06-15T10:00:00.000Z');
expect(schedule.end_time.toISOString()).toBe('2024-06-15T12:00:00.000Z');
expect(schedule.is_fixed).toBe(true);
expect(schedule.expiry_date).toBeNull();
// chatController.createChatRoomInternal이 호출되지 않았는지 확인 (이미 모임 생성 시 호출됨)
expect(chatController.createChatRoomInternal).toHaveBeenCalledTimes(1);
});
test('모임 참가 시 스케줄이 겹치는 경우 참가를 실패해야 한다', async () => {
// Alice가 모임 생성
const meetingData1 = {
title: 'Morning Yoga',
description: 'Start your day with yoga.',
start_time: '2024-07-01T06:00:00Z',
end_time: '2024-07-01T07:00:00Z',
location: 'Gym',
deadline: '2024-06-30T23:59:59Z',
type: 'OPEN',
created_by: 1,
};
const { meeting_id: meetingId1 } = await MeetingService.createMeeting(meetingData1);
// Bob의 기존 스케줄 생성
await ScheduleService.createSchedule({
userId: 2,
title: 'Work',
start_time: new Date('2024-07-01T06:30:00Z'),
end_time: new Date('2024-07-01T08:30:00Z'),
is_fixed: true,
}, null); // 트랜잭션 없이 생성
// Bob가 모임에 참가 시도
await expect(MeetingService.joinMeeting(meetingId1, 2))
.rejects
.toThrow('스케줄이 겹칩니다. 다른 모임에 참가하세요.');
});
test('모임 참가 시 이미 참가한 사용자는 다시 참가할 수 없어야 한다', async () => {
// Alice가 모임 생성
const meetingData = {
title: 'Evening Run',
description: 'Join us for an evening run.',
start_time: '2024-08-20T18:00:00Z',
end_time: '2024-08-20T19:00:00Z',
location: 'Park',
deadline: '2024-08-19T23:59:59Z',
type: 'OPEN',
created_by: 1,
};
const { meeting_id } = await MeetingService.createMeeting(meetingData);
// Bob가 모임에 참가
await MeetingService.joinMeeting(meeting_id, 2);
// Bob가 다시 모임에 참가하려 시도
await expect(MeetingService.joinMeeting(meeting_id, 2))
.rejects
.toThrow('이미 참가한 사용자입니다.');
});
test('모임 마감된 경우 참가를 실패해야 한다', async () => {
// Alice가 모임 생성 (이미 마감됨)
const meetingData = {
title: 'Afternoon Workshop',
description: 'A workshop on web development.',
start_time: '2024-09-10T14:00:00Z',
end_time: '2024-09-10T16:00:00Z',
location: 'Office',
deadline: '2024-09-09T23:59:59Z',
type: 'CLOSE',
created_by: 1,
};
const { meeting_id } = await MeetingService.createMeeting(meetingData);
// Bob가 모임에 참가 시도
await expect(MeetingService.joinMeeting(meeting_id, 2))
.rejects
.toThrow('이미 마감된 모임입니다.');
});
});
});