/** * WorkReportService 테스트 * * 작업 보고서 서비스 레이어 단위 테스트 */ const workReportService = require('../../../services/workReportService'); const { ValidationError, NotFoundError, DatabaseError } = require('../../../utils/errors'); const { mockWorkReports } = require('../../helpers/mockData'); // 모델 모킹 jest.mock('../../../models/workReportModel'); const workReportModel = require('../../../models/workReportModel'); describe('WorkReportService', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('createWorkReportService', () => { it('단일 보고서를 성공적으로 생성해야 함', async () => { // Arrange const reportData = { report_date: '2025-12-11', worker_id: 1, project_id: 1, work_type_id: 1, work_hours: 8.0, work_content: '기능 개발' }; // workReportModel.create가 콜백 형태이므로 모킹 설정 workReportModel.create = jest.fn((data, callback) => { callback(null, 123); // insertId = 123 }); // Act const result = await workReportService.createWorkReportService(reportData); // Assert expect(result).toEqual({ workReport_ids: [123] }); expect(workReportModel.create).toHaveBeenCalledTimes(1); expect(workReportModel.create).toHaveBeenCalledWith( reportData, expect.any(Function) ); }); it('다중 보고서를 성공적으로 생성해야 함', async () => { // Arrange const reportsData = [ { report_date: '2025-12-11', worker_id: 1, work_hours: 8 }, { report_date: '2025-12-11', worker_id: 2, work_hours: 7 } ]; let callCount = 0; workReportModel.create = jest.fn((data, callback) => { callCount++; callback(null, 100 + callCount); }); // Act const result = await workReportService.createWorkReportService(reportsData); // Assert expect(result).toEqual({ workReport_ids: [101, 102] }); expect(workReportModel.create).toHaveBeenCalledTimes(2); }); it('빈 배열이면 ValidationError를 던져야 함', async () => { // Act & Assert await expect(workReportService.createWorkReportService([])) .rejects.toThrow(ValidationError); await expect(workReportService.createWorkReportService([])) .rejects.toThrow('보고서 데이터가 필요합니다'); }); it('DB 오류 시 DatabaseError를 던져야 함', async () => { // Arrange const reportData = { report_date: '2025-12-11', worker_id: 1 }; workReportModel.create = jest.fn((data, callback) => { callback(new Error('DB connection failed'), null); }); // Act & Assert await expect(workReportService.createWorkReportService(reportData)) .rejects.toThrow(DatabaseError); }); }); describe('getWorkReportsByDateService', () => { it('날짜로 보고서를 조회해야 함', async () => { // Arrange const date = '2025-12-11'; const mockReports = mockWorkReports.filter(r => r.report_date === date); workReportModel.getAllByDate = jest.fn((date, callback) => { callback(null, mockReports); }); // Act const result = await workReportService.getWorkReportsByDateService(date); // Assert expect(result).toEqual(mockReports); expect(workReportModel.getAllByDate).toHaveBeenCalledWith(date, expect.any(Function)); }); it('날짜가 없으면 ValidationError를 던져야 함', async () => { // Act & Assert await expect(workReportService.getWorkReportsByDateService(null)) .rejects.toThrow(ValidationError); await expect(workReportService.getWorkReportsByDateService(null)) .rejects.toThrow('날짜가 필요합니다'); }); it('DB 오류 시 DatabaseError를 던져야 함', async () => { // Arrange workReportModel.getAllByDate = jest.fn((date, callback) => { callback(new Error('DB error'), null); }); // Act & Assert await expect(workReportService.getWorkReportsByDateService('2025-12-11')) .rejects.toThrow(DatabaseError); }); }); describe('getWorkReportByIdService', () => { it('ID로 보고서를 조회해야 함', async () => { // Arrange const mockReport = mockWorkReports[0]; workReportModel.getById = jest.fn((id, callback) => { callback(null, mockReport); }); // Act const result = await workReportService.getWorkReportByIdService(1); // Assert expect(result).toEqual(mockReport); expect(workReportModel.getById).toHaveBeenCalledWith(1, expect.any(Function)); }); it('보고서가 없으면 NotFoundError를 던져야 함', async () => { // Arrange workReportModel.getById = jest.fn((id, callback) => { callback(null, null); }); // Act & Assert await expect(workReportService.getWorkReportByIdService(999)) .rejects.toThrow(NotFoundError); await expect(workReportService.getWorkReportByIdService(999)) .rejects.toThrow('작업 보고서를 찾을 수 없습니다'); }); it('ID가 없으면 ValidationError를 던져야 함', async () => { // Act & Assert await expect(workReportService.getWorkReportByIdService(null)) .rejects.toThrow(ValidationError); }); }); describe('updateWorkReportService', () => { it('보고서를 성공적으로 수정해야 함', async () => { // Arrange const updateData = { work_hours: 9 }; workReportModel.update = jest.fn((id, data, callback) => { callback(null, 1); // affectedRows = 1 }); // Act const result = await workReportService.updateWorkReportService(123, updateData); // Assert expect(result).toEqual({ changes: 1 }); expect(workReportModel.update).toHaveBeenCalledWith( 123, updateData, expect.any(Function) ); }); it('수정할 보고서가 없으면 NotFoundError를 던져야 함', async () => { // Arrange workReportModel.update = jest.fn((id, data, callback) => { callback(null, 0); // affectedRows = 0 }); // Act & Assert await expect(workReportService.updateWorkReportService(999, {})) .rejects.toThrow(NotFoundError); }); it('ID가 없으면 ValidationError를 던져야 함', async () => { // Act & Assert await expect(workReportService.updateWorkReportService(null, {})) .rejects.toThrow(ValidationError); }); }); describe('removeWorkReportService', () => { it('보고서를 성공적으로 삭제해야 함', async () => { // Arrange workReportModel.remove = jest.fn((id, callback) => { callback(null, 1); }); // Act const result = await workReportService.removeWorkReportService(123); // Assert expect(result).toEqual({ changes: 1 }); expect(workReportModel.remove).toHaveBeenCalledWith(123, expect.any(Function)); }); it('삭제할 보고서가 없으면 NotFoundError를 던져야 함', async () => { // Arrange workReportModel.remove = jest.fn((id, callback) => { callback(null, 0); }); // Act & Assert await expect(workReportService.removeWorkReportService(999)) .rejects.toThrow(NotFoundError); }); it('ID가 없으면 ValidationError를 던져야 함', async () => { // Act & Assert await expect(workReportService.removeWorkReportService(null)) .rejects.toThrow(ValidationError); }); }); describe('getWorkReportsInRangeService', () => { it('기간으로 보고서를 조회해야 함', async () => { // Arrange const start = '2025-12-01'; const end = '2025-12-31'; workReportModel.getByRange = jest.fn((start, end, callback) => { callback(null, mockWorkReports); }); // Act const result = await workReportService.getWorkReportsInRangeService(start, end); // Assert expect(result).toEqual(mockWorkReports); expect(workReportModel.getByRange).toHaveBeenCalledWith( start, end, expect.any(Function) ); }); it('시작일이 없으면 ValidationError를 던져야 함', async () => { // Act & Assert await expect(workReportService.getWorkReportsInRangeService(null, '2025-12-31')) .rejects.toThrow(ValidationError); }); it('종료일이 없으면 ValidationError를 던져야 함', async () => { // Act & Assert await expect(workReportService.getWorkReportsInRangeService('2025-12-01', null)) .rejects.toThrow(ValidationError); }); }); describe('getSummaryService', () => { it('월간 요약을 조회해야 함', async () => { // Arrange const year = '2025'; const month = '12'; workReportModel.getByRange = jest.fn((start, end, callback) => { callback(null, mockWorkReports); }); // Act const result = await workReportService.getSummaryService(year, month); // Assert expect(result).toEqual(mockWorkReports); expect(workReportModel.getByRange).toHaveBeenCalled(); }); it('연도가 없으면 ValidationError를 던져야 함', async () => { // Act & Assert await expect(workReportService.getSummaryService(null, '12')) .rejects.toThrow(ValidationError); }); it('월이 없으면 ValidationError를 던져야 함', async () => { // Act & Assert await expect(workReportService.getSummaryService('2025', null)) .rejects.toThrow(ValidationError); }); it('데이터가 없으면 NotFoundError를 던져야 함', async () => { // Arrange workReportModel.getByRange = jest.fn((start, end, callback) => { callback(null, []); }); // Act & Assert await expect(workReportService.getSummaryService('2025', '12')) .rejects.toThrow(NotFoundError); await expect(workReportService.getSummaryService('2025', '12')) .rejects.toThrow('해당 기간의 작업 보고서가 없습니다'); }); }); });