Files
TK-FB-Project/deploy/tkfb-package/api.hyungi.net/tests/unit/services/workReportService.test.js
Hyungi Ahn 2b1c7bfb88 feat: 다수 기능 개선 - 순찰, 출근, 작업분석, 모바일 UI 등
- 순찰/점검 기능 개선 (zone-detail 페이지 추가)
- 출근/근태 시스템 개선 (연차 조회, 근무현황)
- 작업분석 대분류 그룹화 및 마이그레이션 스크립트
- 모바일 네비게이션 UI 추가
- NAS 배포 도구 및 문서 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 14:41:01 +09:00

322 lines
10 KiB
JavaScript

/**
* 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('해당 기간의 작업 보고서가 없습니다');
});
});
});