Skip to content
Snippets Groups Projects
meetingService.test.js 12.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • // 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('이미 마감된 모임입니다.');
            });
        });
    });