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
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');
...
@@ -4,7 +4,7 @@ const sequelize = require('../config/sequelize');
const
User
=
require
(
'
../models/User
'
);
const
User
=
require
(
'
../models/User
'
);
const
Friend
=
require
(
'
../models/Friend
'
);
const
Friend
=
require
(
'
../models/Friend
'
);
const
Schedule
=
require
(
'
../models/Schedule
'
);
const
Schedule
=
require
(
'
../models/Schedule
'
);
const
scheduleService
=
require
(
'
.
./services
/scheduleService
'
);
// scheduleService 임포트
const
scheduleService
=
require
(
'
./scheduleService
'
);
// scheduleService 임포트
beforeAll
(
async
()
=>
{
beforeAll
(
async
()
=>
{
await
sequelize
.
sync
({
force
:
true
});
await
sequelize
.
sync
({
force
:
true
});
...
@@ -61,6 +61,7 @@ describe('Schedule Service', () => {
...
@@ -61,6 +61,7 @@ describe('Schedule Service', () => {
};
};
const
schedule
=
await
scheduleService
.
createSchedule
(
scheduleData
);
const
schedule
=
await
scheduleService
.
createSchedule
(
scheduleData
);
expect
(
schedule
).
toBeDefined
();
expect
(
schedule
).
toBeDefined
();
expect
(
schedule
.
user_id
).
toBe
(
2
);
expect
(
schedule
.
user_id
).
toBe
(
2
);
...
@@ -156,19 +157,19 @@ describe('Schedule Service', () => {
...
@@ -156,19 +157,19 @@ describe('Schedule Service', () => {
describe
(
'
deleteSchedule
'
,
()
=>
{
describe
(
'
deleteSchedule
'
,
()
=>
{
test
(
'
should delete an existing schedule successfully
'
,
async
()
=>
{
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
);
const
schedule
=
await
Schedule
.
findByPk
(
2
);
expect
(
schedule
).
toBeNull
();
expect
(
schedule
).
toBeNull
();
});
});
test
(
'
should throw error when deleting a non-existing schedule
'
,
async
()
=>
{
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
'
,
()
=>
{
describe
(
'
getAllSchedules
'
,
()
=>
{
test
(
'
should retrieve all valid schedules for a user
'
,
async
()
=>
{
test
(
'
should retrieve all valid schedules for a user
'
,
async
()
=>
{
...
@@ -192,42 +193,52 @@ describe('Schedule Service', () => {
...
@@ -192,42 +193,52 @@ describe('Schedule Service', () => {
await
expect
(
scheduleService
.
getScheduleById
(
999
,
1
)).
rejects
.
toThrow
(
'
Schedule not found
'
);
await
expect
(
scheduleService
.
getScheduleById
(
999
,
1
)).
rejects
.
toThrow
(
'
Schedule not found
'
);
});
});
});
});
// test/schedule.test.js
describe
(
'
cleanExpiredSchedules
'
,
()
=>
{
describe
(
'
cleanExpiredSchedules
'
,
()
=>
{
test
(
'
should delete expired flexible schedules
'
,
async
()
=>
{
test
(
'
should delete expired flexible schedules
'
,
async
()
=>
{
// 만료된 유동 스케줄 생성
// 현재 날짜를 기준으로 만료된 스케줄과 만료되지 않은 스케줄 생성
await
Schedule
.
create
({
const
now
=
new
Date
(
'
2024-05-07T00:00:00Z
'
);
// 테스트를 위한 고정된 현재 날짜
id
:
3
,
user_id
:
1
,
// Jest의 Fake Timers를 사용하여 Date를 고정
title
:
'
Expired Flexible Schedule
'
,
jest
.
useFakeTimers
(
'
modern
'
);
start_time
:
new
Date
(
'
2024-04-25T10:00:00Z
'
),
jest
.
setSystemTime
(
now
);
end_time
:
new
Date
(
'
2024-04-25T11:00:00Z
'
),
is_fixed
:
false
,
// 만료된 유동 스케줄 생성
expiry_date
:
new
Date
(
'
2024-04-30T00:00:00Z
'
),
// 이미 만료됨
await
Schedule
.
create
({
});
user_id
:
1
,
title
:
'
Expired Flexible Schedule
'
,
// 만료되지 않은 유동 스케줄 생성
start_time
:
new
Date
(
'
2024-04-25T10:00:00Z
'
),
await
Schedule
.
create
({
end_time
:
new
Date
(
'
2024-04-25T11:00:00Z
'
),
id
:
4
,
is_fixed
:
false
,
user_id
:
1
,
expiry_date
:
new
Date
(
'
2024-05-06T00:00:00Z
'
),
// 이미 만료됨
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
,
await
Schedule
.
create
({
expiry_date
:
new
Date
(
'
2024-05-14T00:00:00Z
'
),
// 아직 만료되지 않음
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
'
),
await
scheduleService
.
cleanExpiredSchedules
();
is_fixed
:
false
,
expiry_date
:
new
Date
(
'
2024-05-14T00:00:00Z
'
),
// 아직 만료되지 않음
// 만료된 스케줄이 삭제되었는지 확인
});
const
expiredSchedule
=
await
Schedule
.
findByPk
(
3
);
expect
(
expiredSchedule
).
toBeNull
();
// 만료된 스케줄 정리
await
scheduleService
.
cleanExpiredSchedules
();
// 만료되지 않은 스케줄은 남아있는지 확인
const
validSchedule
=
await
Schedule
.
findByPk
(
4
);
// 만료된 스케줄이 삭제되었는지 확인
expect
(
validSchedule
).
toBeDefined
();
const
expiredSchedule
=
await
Schedule
.
findOne
({
where
:
{
title
:
'
Expired Flexible Schedule
'
}
});
expect
(
validSchedule
.
title
).
toBe
(
'
Valid 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');
...
@@ -5,237 +5,257 @@ const Schedule = require('../models/Schedule');
const
ScheduleResponseDTO
=
require
(
'
../dtos/ScheduleResponseDTO
'
);
const
ScheduleResponseDTO
=
require
(
'
../dtos/ScheduleResponseDTO
'
);
class
scheduleService
{
class
scheduleService
{
/**
/**
* 트랜잭션 래퍼 함수
* 트랜잭션 래퍼 함수
*/
*/
async
withTransaction
(
callback
)
{
async
withTransaction
(
callback
)
{
const
transaction
=
await
Schedule
.
sequelize
.
transaction
();
const
transaction
=
await
Schedule
.
sequelize
.
transaction
();
try
{
try
{
const
result
=
await
callback
(
transaction
);
const
result
=
await
callback
(
transaction
);
await
transaction
.
commit
();
await
transaction
.
commit
();
return
result
;
return
result
;
}
catch
(
error
)
{
}
catch
(
error
)
{
await
transaction
.
rollback
();
await
transaction
.
rollback
();
throw
error
;
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
;
}
}
}
/**
* 스케줄 유효성 검사
/**
*/
* 공통 where 절 생성
validateScheduleTime
(
start_time
,
end_time
)
{
*/
if
(
new
Date
(
start_time
)
>=
new
Date
(
end_time
))
{
getScheduleWhereClause
(
userId
,
id
=
null
)
{
throw
new
Error
(
'
Start time must be before end time
'
);
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
;
* 유동 스케줄 만료일 구하기
}
*/
getNextMonday
(
startTime
)
{
const
date
=
new
Date
(
startTime
);
const
day
=
date
.
getDay
();
const
daysUntilNextMonday
=
(
7
-
day
+
1
)
%
7
;
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
)
=>
{
getNextMonday
(
startTime
)
{
this
.
validateScheduleTime
(
start_time
,
end_time
);
const
date
=
new
Date
(
startTime
);
const
overlap
=
await
this
.
checkScheduleOverlap
(
userId
,
start_time
,
end_time
);
const
day
=
date
.
getUTCDay
();
// 0=Sunday, 1=Monday, ..., 6=Saturday
if
(
overlap
)
{
const
daysUntilNextMonday
=
(
8
-
day
)
%
7
||
7
;
// Ensure next Monday
throw
new
Error
(
'
Schedule overlaps with existing schedule
'
);
}
const
nextMonday
=
new
Date
(
Date
.
UTC
(
const
scheduleData
=
{
date
.
getUTCFullYear
(),
user_id
:
userId
,
date
.
getUTCMonth
(),
title
,
date
.
getUTCDate
()
+
daysUntilNextMonday
,
start_time
,
0
,
end_time
,
0
,
is_fixed
,
0
,
expiry_date
:
is_fixed
?
null
:
this
.
getNextMonday
(
start_time
)
0
// Set to midnight UTC
};
)
);
return
Schedule
.
create
(
scheduleData
,
{
transaction
});
});
return
nextMonday
;
}
return
new
ScheduleResponseDTO
(
schedule
);
/**
* 사용자 스케줄 생성
*/
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
)
=>
{
async
getScheduleById
(
id
,
userId
)
{
const
schedule
=
await
Schedule
.
findOne
({
try
{
where
:
{
id
,
user_id
:
userId
},
const
schedule
=
await
Schedule
.
findOne
({
transaction
where
:
this
.
getScheduleWhereClause
(
userId
,
id
),
});
});
if
(
!
schedule
)
{
if
(
!
schedule
)
{
throw
new
Error
(
'
Schedule not found
'
);
throw
new
Error
(
"
Schedule not found
"
);
}
}
this
.
validateScheduleTime
(
updateData
.
start_time
,
updateData
.
end_time
);
return
new
ScheduleResponseDTO
(
schedule
);
}
catch
(
error
)
{
const
overlap
=
await
this
.
checkScheduleOverlap
(
throw
new
Error
(
`Failed to fetch schedule:
${
error
.
message
}
`
);
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
deleteSchedule
(
id
,
userId
)
{
*/
return
this
.
withTransaction
(
async
(
transaction
)
=>
{
async
cleanExpiredSchedules
()
{
const
result
=
await
Schedule
.
destroy
({
try
{
where
:
{
id
,
user_id
:
userId
},
await
Schedule
.
destroy
({
transaction
where
:
{
});
is_fixed
:
false
,
expiry_date
:
{
[
Op
.
lte
]:
new
Date
()
},
if
(
!
result
)
{
},
throw
new
Error
(
'
Schedule not found
'
);
});
}
}
catch
(
error
)
{
throw
new
Error
(
`Failed to clean expired schedules:
${
error
.
message
}
`
);
// 삭제 성공 메시지 반환
return
{
message
:
'
Schedule successfully deleted
'
};
});
}
}
}
/**
* 해당 사용자의 스케줄 정보 조회
/**
*/
* 스케줄 중복 검사
async
getAllSchedules
(
userId
)
{
*/
try
{
async
checkScheduleOverlap
(
userId
,
start_time
,
end_time
,
excludeId
=
null
)
{
const
schedules
=
await
Schedule
.
findAll
({
try
{
where
:
this
.
getScheduleWhereClause
(
userId
),
const
where
=
{
order
:
[[
'
start_time
'
,
'
ASC
'
]]
user_id
:
userId
,
});
[
Op
.
or
]:
[
const
schedulesDTO
=
schedules
.
map
(
schedule
=>
new
ScheduleResponseDTO
(
schedule
));
{
return
schedulesDTO
;
[
Op
.
and
]:
[
}
catch
(
error
)
{
{
start_time
:
{
[
Op
.
lte
]:
start_time
}
},
throw
new
Error
(
`Failed to fetch schedules:
${
error
.
message
}
`
);
{
end_time
:
{
[
Op
.
gte
]:
start_time
}
},
}
],
}
},
{
/**
[
Op
.
and
]:
[
* 해당 사용자의 특정 스케줄 조회
{
start_time
:
{
[
Op
.
gte
]:
start_time
}
},
*/
{
start_time
:
{
[
Op
.
lte
]:
end_time
}
},
async
getScheduleById
(
id
,
userId
)
{
],
try
{
},
const
schedule
=
await
Schedule
.
findOne
({
],
where
:
this
.
getScheduleWhereClause
(
userId
,
id
)
};
});
if
(
excludeId
)
{
if
(
!
schedule
)
{
where
.
id
=
{
[
Op
.
ne
]:
excludeId
};
throw
new
Error
(
'
Schedule not found
'
);
}
}
const
overlappingSchedule
=
await
Schedule
.
findOne
({
where
});
return
new
ScheduleResponseDTO
(
schedule
);
return
overlappingSchedule
;
}
catch
(
error
)
{
}
catch
(
error
)
{
throw
new
Error
(
`Failed to fetch schedule:
${
error
.
message
}
`
);
throw
new
Error
(
`Failed to check schedule overlap:
${
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
}
`
);
}
}
}
}
}
}
module
.
exports
=
new
scheduleService
();
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