- 순찰/점검 기능 개선 (zone-detail 페이지 추가) - 출근/근태 시스템 개선 (연차 조회, 근무현황) - 작업분석 대분류 그룹화 및 마이그레이션 스크립트 - 모바일 네비게이션 UI 추가 - NAS 배포 도구 및 문서 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
322 lines
10 KiB
JavaScript
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('해당 기간의 작업 보고서가 없습니다');
|
|
});
|
|
});
|
|
});
|