Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
W
WebBack
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
websystem
WebBack
Commits
490d41b6
Commit
490d41b6
authored
3 months ago
by
tpgus2603
Browse files
Options
Downloads
Patches
Plain Diff
test,refactor 서비스로직 관련 테스트 및 리팩토링
parent
fd2daa22
No related branches found
Branches containing commit
No related tags found
2 merge requests
!31
Develop
,
!14
[#11] dto설정, 프렌드,서비스로직 테스트 및 로직변경
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
services/schedule.test.js
+53
-42
53 additions, 42 deletions
services/schedule.test.js
services/scheduleService.js
+242
-222
242 additions, 222 deletions
services/scheduleService.js
with
295 additions
and
264 deletions
services/schedule.test.js
+
53
−
42
View file @
490d41b6
...
...
@@ -4,7 +4,7 @@ const sequelize = require('../config/sequelize');
const
User
=
require
(
'
../models/User
'
);
const
Friend
=
require
(
'
../models/Friend
'
);
const
Schedule
=
require
(
'
../models/Schedule
'
);
const
scheduleService
=
require
(
'
.
./services
/scheduleService
'
);
// scheduleService 임포트
const
scheduleService
=
require
(
'
./scheduleService
'
);
// scheduleService 임포트
beforeAll
(
async
()
=>
{
await
sequelize
.
sync
({
force
:
true
});
...
...
@@ -61,6 +61,7 @@ describe('Schedule Service', () => {
};
const
schedule
=
await
scheduleService
.
createSchedule
(
scheduleData
);
expect
(
schedule
).
toBeDefined
();
expect
(
schedule
.
user_id
).
toBe
(
2
);
...
...
@@ -156,19 +157,19 @@ describe('Schedule Service', () => {
describe
(
'
deleteSchedule
'
,
()
=>
{
test
(
'
should delete an existing schedule successfully
'
,
async
()
=>
{
const
result
=
await
scheduleService
.
deleteSchedule
(
2
,
1
);
const
result
=
await
scheduleService
.
deleteSchedule
(
2
,
1
);
expect
(
result
).
to
Be
(
true
);
expect
(
result
).
to
Equal
({
message
:
'
Schedule successfully deleted
'
}
);
// 삭제된 스케줄이 실제로 삭제되었는지 확인
const
schedule
=
await
Schedule
.
findByPk
(
2
);
expect
(
schedule
).
toBeNull
();
// 삭제된 스케줄이 실제로 삭제되었는지 확인
const
schedule
=
await
Schedule
.
findByPk
(
2
);
expect
(
schedule
).
toBeNull
();
});
test
(
'
should throw error when deleting a non-existing schedule
'
,
async
()
=>
{
await
expect
(
scheduleService
.
deleteSchedule
(
999
,
1
)).
rejects
.
toThrow
(
'
Schedule not found
'
);
await
expect
(
scheduleService
.
deleteSchedule
(
999
,
1
)).
rejects
.
toThrow
(
'
Schedule not found
'
);
});
});
});
describe
(
'
getAllSchedules
'
,
()
=>
{
test
(
'
should retrieve all valid schedules for a user
'
,
async
()
=>
{
...
...
@@ -192,42 +193,52 @@ describe('Schedule Service', () => {
await
expect
(
scheduleService
.
getScheduleById
(
999
,
1
)).
rejects
.
toThrow
(
'
Schedule not found
'
);
});
});
// test/schedule.test.js
describe
(
'
cleanExpiredSchedules
'
,
()
=>
{
test
(
'
should delete expired flexible schedules
'
,
async
()
=>
{
// 만료된 유동 스케줄 생성
await
Schedule
.
create
({
id
:
3
,
user_id
:
1
,
title
:
'
Expired Flexible Schedule
'
,
start_time
:
new
Date
(
'
2024-04-25T10:00:00Z
'
),
end_time
:
new
Date
(
'
2024-04-25T11:00:00Z
'
),
is_fixed
:
false
,
expiry_date
:
new
Date
(
'
2024-04-30T00:00:00Z
'
),
// 이미 만료됨
});
// 만료되지 않은 유동 스케줄 생성
await
Schedule
.
create
({
id
:
4
,
user_id
:
1
,
title
:
'
Valid Flexible Schedule
'
,
start_time
:
new
Date
(
'
2024-05-07T10:00:00Z
'
),
end_time
:
new
Date
(
'
2024-05-07T11:00:00Z
'
),
is_fixed
:
false
,
expiry_date
:
new
Date
(
'
2024-05-14T00:00:00Z
'
),
// 아직 만료되지 않음
});
// 만료된 스케줄 정리
await
scheduleService
.
cleanExpiredSchedules
();
// 만료된 스케줄이 삭제되었는지 확인
const
expiredSchedule
=
await
Schedule
.
findByPk
(
3
);
expect
(
expiredSchedule
).
toBeNull
();
// 만료되지 않은 스케줄은 남아있는지 확인
const
validSchedule
=
await
Schedule
.
findByPk
(
4
);
expect
(
validSchedule
).
toBeDefined
();
expect
(
validSchedule
.
title
).
toBe
(
'
Valid Flexible Schedule
'
);
// 현재 날짜를 기준으로 만료된 스케줄과 만료되지 않은 스케줄 생성
const
now
=
new
Date
(
'
2024-05-07T00:00:00Z
'
);
// 테스트를 위한 고정된 현재 날짜
// Jest의 Fake Timers를 사용하여 Date를 고정
jest
.
useFakeTimers
(
'
modern
'
);
jest
.
setSystemTime
(
now
);
// 만료된 유동 스케줄 생성
await
Schedule
.
create
({
user_id
:
1
,
title
:
'
Expired Flexible Schedule
'
,
start_time
:
new
Date
(
'
2024-04-25T10:00:00Z
'
),
end_time
:
new
Date
(
'
2024-04-25T11:00:00Z
'
),
is_fixed
:
false
,
expiry_date
:
new
Date
(
'
2024-05-06T00:00:00Z
'
),
// 이미 만료됨
});
// 만료되지 않은 유동 스케줄 생성
await
Schedule
.
create
({
user_id
:
1
,
title
:
'
Valid Flexible Schedule
'
,
start_time
:
new
Date
(
'
2024-05-07T10:00:00Z
'
),
end_time
:
new
Date
(
'
2024-05-07T11:00:00Z
'
),
is_fixed
:
false
,
expiry_date
:
new
Date
(
'
2024-05-14T00:00:00Z
'
),
// 아직 만료되지 않음
});
// 만료된 스케줄 정리
await
scheduleService
.
cleanExpiredSchedules
();
// 만료된 스케줄이 삭제되었는지 확인
const
expiredSchedule
=
await
Schedule
.
findOne
({
where
:
{
title
:
'
Expired Flexible Schedule
'
}
});
expect
(
expiredSchedule
).
toBeNull
();
// 만료되지 않은 스케줄은 남아있는지 확인
const
validSchedule
=
await
Schedule
.
findOne
({
where
:
{
title
:
'
Valid Flexible Schedule
'
}
});
expect
(
validSchedule
).
toBeDefined
();
expect
(
validSchedule
.
title
).
toBe
(
'
Valid Flexible Schedule
'
);
// Jest의 Fake Timers를 복구
jest
.
useRealTimers
();
});
});
});
});
This diff is collapsed.
Click to expand it.
services/scheduleService.js
+
242
−
222
View file @
490d41b6
...
...
@@ -5,237 +5,257 @@ const Schedule = require('../models/Schedule');
const
ScheduleResponseDTO
=
require
(
'
../dtos/ScheduleResponseDTO
'
);
class
scheduleService
{
/**
* 트랜잭션 래퍼 함수
*/
async
withTransaction
(
callback
)
{
const
transaction
=
await
Schedule
.
sequelize
.
transaction
();
try
{
const
result
=
await
callback
(
transaction
);
await
transaction
.
commit
();
return
result
;
}
catch
(
error
)
{
await
transaction
.
rollback
();
throw
error
;
}
}
/**
* 공통 where 절 생성
*/
getScheduleWhereClause
(
userId
,
id
=
null
)
{
const
where
=
{
user_id
:
userId
,
[
Op
.
or
]:
[
{
is_fixed
:
true
},
{
is_fixed
:
false
,
expiry_date
:
{
[
Op
.
gt
]:
new
Date
()
}
}
]
};
if
(
id
)
{
where
.
id
=
id
;
}
return
where
;
/**
* 트랜잭션 래퍼 함수
*/
async
withTransaction
(
callback
)
{
const
transaction
=
await
Schedule
.
sequelize
.
transaction
();
try
{
const
result
=
await
callback
(
transaction
);
await
transaction
.
commit
();
return
result
;
}
catch
(
error
)
{
await
transaction
.
rollback
();
throw
error
;
}
/**
* 스케줄 유효성 검사
*/
validateScheduleTime
(
start_time
,
end_time
)
{
if
(
new
Date
(
start_time
)
>=
new
Date
(
end_time
))
{
throw
new
Error
(
'
Start time must be before end time
'
);
}
}
/**
* 공통 where 절 생성
*/
getScheduleWhereClause
(
userId
,
id
=
null
)
{
const
where
=
{
user_id
:
userId
,
[
Op
.
or
]:
[
{
is_fixed
:
true
},
{
is_fixed
:
false
,
expiry_date
:
{
[
Op
.
gt
]:
new
Date
()
},
},
],
};
if
(
id
)
{
where
.
id
=
id
;
}
/**
* 유동 스케줄 만료일 구하기
*/
getNextMonday
(
startTime
)
{
const
date
=
new
Date
(
startTime
);
const
day
=
date
.
getDay
();
const
daysUntilNextMonday
=
(
7
-
day
+
1
)
%
7
;
return
where
;
}
const
nextMonday
=
new
Date
(
date
);
nextMonday
.
setDate
(
date
.
getDate
()
+
daysUntilNextMonday
);
nextMonday
.
setHours
(
0
,
0
,
0
,
0
);
// 자정으로 설정
return
nextMonday
;
/**
* 스케줄 유효성 검사
* 이미 컨트롤러에서 검증했으므로, 추가 검증 필요 시 수행
*/
validateScheduleTime
(
start_time
,
end_time
)
{
if
(
new
Date
(
start_time
)
>=
new
Date
(
end_time
))
{
throw
new
Error
(
"
Start time must be before end time
"
);
}
/**
* 사용자 스케줄 생성
*/
async
createSchedule
({
userId
,
title
,
start_time
,
end_time
,
is_fixed
})
{
const
schedule
=
await
this
.
withTransaction
(
async
(
transaction
)
=>
{
this
.
validateScheduleTime
(
start_time
,
end_time
);
const
overlap
=
await
this
.
checkScheduleOverlap
(
userId
,
start_time
,
end_time
);
if
(
overlap
)
{
throw
new
Error
(
'
Schedule overlaps with existing schedule
'
);
}
const
scheduleData
=
{
user_id
:
userId
,
title
,
start_time
,
end_time
,
is_fixed
,
expiry_date
:
is_fixed
?
null
:
this
.
getNextMonday
(
start_time
)
};
return
Schedule
.
create
(
scheduleData
,
{
transaction
});
});
return
new
ScheduleResponseDTO
(
schedule
);
}
/**
* 유동 스케줄 만료일 구하기
*/
getNextMonday
(
startTime
)
{
const
date
=
new
Date
(
startTime
);
const
day
=
date
.
getUTCDay
();
// 0=Sunday, 1=Monday, ..., 6=Saturday
const
daysUntilNextMonday
=
(
8
-
day
)
%
7
||
7
;
// Ensure next Monday
const
nextMonday
=
new
Date
(
Date
.
UTC
(
date
.
getUTCFullYear
(),
date
.
getUTCMonth
(),
date
.
getUTCDate
()
+
daysUntilNextMonday
,
0
,
0
,
0
,
0
// Set to midnight UTC
)
);
return
nextMonday
;
}
/**
* 사용자 스케줄 생성
*/
async
createSchedule
({
userId
,
title
,
start_time
,
end_time
,
is_fixed
})
{
const
schedule
=
await
this
.
withTransaction
(
async
(
transaction
)
=>
{
this
.
validateScheduleTime
(
start_time
,
end_time
);
const
overlap
=
await
this
.
checkScheduleOverlap
(
userId
,
start_time
,
end_time
);
if
(
overlap
)
{
throw
new
Error
(
"
Schedule overlaps with existing schedule
"
);
}
const
scheduleData
=
{
user_id
:
userId
,
title
,
start_time
,
end_time
,
is_fixed
,
expiry_date
:
is_fixed
?
null
:
this
.
getNextMonday
(
start_time
),
};
return
Schedule
.
create
(
scheduleData
,
{
transaction
});
});
return
new
ScheduleResponseDTO
(
schedule
);
}
/**
* 사용자 스케줄 수정
*/
async
updateSchedule
(
id
,
userId
,
updateData
)
{
const
updatedSchedule
=
await
this
.
withTransaction
(
async
(
transaction
)
=>
{
const
schedule
=
await
Schedule
.
findOne
({
where
:
{
id
,
user_id
:
userId
},
transaction
,
});
if
(
!
schedule
)
{
throw
new
Error
(
"
Schedule not found
"
);
}
// 이미 컨트롤러에서 검증했으므로, 추가 검증이 필요하다면 수행
if
(
updateData
.
start_time
&&
updateData
.
end_time
)
{
this
.
validateScheduleTime
(
updateData
.
start_time
,
updateData
.
end_time
);
}
const
overlap
=
await
this
.
checkScheduleOverlap
(
userId
,
updateData
.
start_time
||
schedule
.
start_time
,
updateData
.
end_time
||
schedule
.
end_time
,
id
);
if
(
overlap
)
{
throw
new
Error
(
"
Schedule overlaps with existing schedule
"
);
}
const
is_fixed
=
schedule
.
is_fixed
;
const
updatedDataWithExpiry
=
{
...
updateData
,
expiry_date
:
is_fixed
?
null
:
this
.
getNextMonday
(
updateData
.
start_time
||
schedule
.
start_time
),
updatedAt
:
new
Date
(),
};
delete
updatedDataWithExpiry
.
is_fixed
;
return
schedule
.
update
(
updatedDataWithExpiry
,
{
transaction
});
});
return
new
ScheduleResponseDTO
(
updatedSchedule
);
}
/**
* 사용자 스케줄 삭제
*/
async
deleteSchedule
(
id
,
userId
)
{
return
this
.
withTransaction
(
async
(
transaction
)
=>
{
const
result
=
await
Schedule
.
destroy
({
where
:
{
id
,
user_id
:
userId
},
transaction
,
});
if
(
!
result
)
{
throw
new
Error
(
"
Schedule not found
"
);
}
// 삭제 성공 메시지 반환
return
{
message
:
"
Schedule successfully deleted
"
};
});
}
/**
* 해당 사용자의 스케줄 정보 조회
*/
async
getAllSchedules
(
userId
)
{
try
{
const
schedules
=
await
Schedule
.
findAll
({
where
:
this
.
getScheduleWhereClause
(
userId
),
order
:
[[
"
start_time
"
,
"
ASC
"
]],
});
const
schedulesDTO
=
schedules
.
map
(
(
schedule
)
=>
new
ScheduleResponseDTO
(
schedule
)
);
return
schedulesDTO
;
}
catch
(
error
)
{
throw
new
Error
(
`Failed to fetch schedules:
${
error
.
message
}
`
);
}
/**
* 사용자 스케줄 수정
*/
async
updateSchedule
(
id
,
userId
,
updateData
)
{
const
updatedSchedule
=
await
this
.
withTransaction
(
async
(
transaction
)
=>
{
const
schedule
=
await
Schedule
.
findOne
({
where
:
{
id
,
user_id
:
userId
},
transaction
});
if
(
!
schedule
)
{
throw
new
Error
(
'
Schedule not found
'
);
}
this
.
validateScheduleTime
(
updateData
.
start_time
,
updateData
.
end_time
);
const
overlap
=
await
this
.
checkScheduleOverlap
(
userId
,
updateData
.
start_time
,
updateData
.
end_time
,
id
);
if
(
overlap
)
{
throw
new
Error
(
'
Schedule overlaps with existing schedule
'
);
}
const
is_fixed
=
schedule
.
is_fixed
;
const
updatedDataWithExpiry
=
{
...
updateData
,
expiry_date
:
is_fixed
?
null
:
this
.
getNextMonday
(
updateData
.
start_time
),
updatedAt
:
new
Date
()
};
delete
updatedDataWithExpiry
.
is_fixed
;
return
schedule
.
update
(
updatedDataWithExpiry
,
{
transaction
});
});
return
new
ScheduleResponseDTO
(
updatedSchedule
);
}
/**
* 해당 사용자의 특정 스케줄 조회
*/
async
getScheduleById
(
id
,
userId
)
{
try
{
const
schedule
=
await
Schedule
.
findOne
({
where
:
this
.
getScheduleWhereClause
(
userId
,
id
),
});
if
(
!
schedule
)
{
throw
new
Error
(
"
Schedule not found
"
);
}
return
new
ScheduleResponseDTO
(
schedule
);
}
catch
(
error
)
{
throw
new
Error
(
`Failed to fetch schedule:
${
error
.
message
}
`
);
}
/**
* 사용자 스케줄 삭제
*/
async
deleteSchedule
(
id
,
userId
)
{
return
this
.
withTransaction
(
async
(
transaction
)
=>
{
const
result
=
await
Schedule
.
destroy
({
where
:
{
id
,
user_id
:
userId
},
transaction
});
if
(
!
result
)
{
throw
new
Error
(
'
Schedule not found
'
);
}
// 삭제 성공 메시지 반환
return
{
message
:
'
Schedule successfully deleted
'
};
});
}
/**
* 만료된 유동 스케줄 정리
*/
async
cleanExpiredSchedules
()
{
try
{
await
Schedule
.
destroy
({
where
:
{
is_fixed
:
false
,
expiry_date
:
{
[
Op
.
lte
]:
new
Date
()
},
},
});
}
catch
(
error
)
{
throw
new
Error
(
`Failed to clean expired schedules:
${
error
.
message
}
`
);
}
/**
* 해당 사용자의 스케줄 정보 조회
*/
async
getAllSchedules
(
userId
)
{
try
{
const
schedules
=
await
Schedule
.
findAll
({
where
:
this
.
getScheduleWhereClause
(
userId
),
order
:
[[
'
start_time
'
,
'
ASC
'
]]
});
const
schedulesDTO
=
schedules
.
map
(
schedule
=>
new
ScheduleResponseDTO
(
schedule
));
return
schedulesDTO
;
}
catch
(
error
)
{
throw
new
Error
(
`Failed to fetch schedules:
${
error
.
message
}
`
);
}
}
/**
* 해당 사용자의 특정 스케줄 조회
*/
async
getScheduleById
(
id
,
userId
)
{
try
{
const
schedule
=
await
Schedule
.
findOne
({
where
:
this
.
getScheduleWhereClause
(
userId
,
id
)
});
if
(
!
schedule
)
{
throw
new
Error
(
'
Schedule not found
'
);
}
return
new
ScheduleResponseDTO
(
schedule
);
}
catch
(
error
)
{
throw
new
Error
(
`Failed to fetch schedule:
${
error
.
message
}
`
);
}
}
/**
* 만료된 유동 스케줄 정리
*/
async
cleanExpiredSchedules
()
{
try
{
await
Schedule
.
destroy
({
where
:
{
is_fixed
:
false
,
expiry_date
:
{
[
Op
.
lte
]:
new
Date
()
}
}
});
}
catch
(
error
)
{
throw
new
Error
(
`Failed to clean expired schedules:
${
error
.
message
}
`
);
}
}
/**
* 스케줄 중복 검사
*/
async
checkScheduleOverlap
(
userId
,
start_time
,
end_time
,
excludeId
=
null
)
{
try
{
const
where
=
{
user_id
:
userId
,
[
Op
.
or
]:
[
{
[
Op
.
and
]:
[
{
start_time
:
{
[
Op
.
lte
]:
start_time
}
},
{
end_time
:
{
[
Op
.
gte
]:
start_time
}
}
]
},
{
[
Op
.
and
]:
[
{
start_time
:
{
[
Op
.
gte
]:
start_time
}
},
{
start_time
:
{
[
Op
.
lte
]:
end_time
}
}
]
}
]
};
if
(
excludeId
)
{
where
.
id
=
{
[
Op
.
ne
]:
excludeId
};
}
const
overlappingSchedule
=
await
Schedule
.
findOne
({
where
});
return
overlappingSchedule
;
}
catch
(
error
)
{
throw
new
Error
(
`Failed to check schedule overlap:
${
error
.
message
}
`
);
}
}
/**
* 스케줄 중복 검사
*/
async
checkScheduleOverlap
(
userId
,
start_time
,
end_time
,
excludeId
=
null
)
{
try
{
const
where
=
{
user_id
:
userId
,
[
Op
.
or
]:
[
{
[
Op
.
and
]:
[
{
start_time
:
{
[
Op
.
lte
]:
start_time
}
},
{
end_time
:
{
[
Op
.
gte
]:
start_time
}
},
],
},
{
[
Op
.
and
]:
[
{
start_time
:
{
[
Op
.
gte
]:
start_time
}
},
{
start_time
:
{
[
Op
.
lte
]:
end_time
}
},
],
},
],
};
if
(
excludeId
)
{
where
.
id
=
{
[
Op
.
ne
]:
excludeId
};
}
const
overlappingSchedule
=
await
Schedule
.
findOne
({
where
});
return
overlappingSchedule
;
}
catch
(
error
)
{
throw
new
Error
(
`Failed to check schedule overlap:
${
error
.
message
}
`
);
}
}
}
module
.
exports
=
new
scheduleService
();
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment