Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
b13eb238
Commit
b13eb238
authored
Jan 14, 2021
by
Sarah Yasonik
Committed by
Sean McGivern
Jan 14, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Generate shifts for a timeframe based on on-call rotation params
parent
0941a0b4
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
879 additions
and
3 deletions
+879
-3
ee/app/models/incident_management/oncall_participant.rb
ee/app/models/incident_management/oncall_participant.rb
+2
-0
ee/app/models/incident_management/oncall_rotation.rb
ee/app/models/incident_management/oncall_rotation.rb
+6
-1
ee/lib/incident_management/oncall_shift_generator.rb
ee/lib/incident_management/oncall_shift_generator.rb
+138
-0
ee/spec/lib/incident_management/oncall_shift_generator_spec.rb
...ec/lib/incident_management/oncall_shift_generator_spec.rb
+702
-0
ee/spec/models/incident_management/oncall_participant_spec.rb
...pec/models/incident_management/oncall_participant_spec.rb
+14
-2
ee/spec/models/incident_management/oncall_rotation_spec.rb
ee/spec/models/incident_management/oncall_rotation_spec.rb
+17
-0
No files found.
ee/app/models/incident_management/oncall_participant.rb
View file @
b13eb238
...
...
@@ -11,6 +11,8 @@ module IncidentManagement
belongs_to
:user
,
class_name:
'User'
,
foreign_key: :user_id
has_many
:shifts
,
class_name:
'OncallShift'
,
inverse_of: :participant
,
foreign_key: :participant_id
scope
:ordered_asc
,
->
{
order
(
id: :asc
)
}
# Uniqueness validations added here should be duplicated
# in IncidentManagement::OncallRotation::CreateService
# as bulk insertion skips validations
...
...
ee/app/models/incident_management/oncall_rotation.rb
View file @
b13eb238
...
...
@@ -19,9 +19,14 @@ module IncidentManagement
validates
:name
,
presence:
true
,
uniqueness:
{
scope: :oncall_schedule_id
},
length:
{
maximum:
NAME_LENGTH
}
validates
:starts_at
,
presence:
true
validates
:length
,
presence:
true
validates
:length
,
presence:
true
,
numericality:
true
validates
:length_unit
,
presence:
true
delegate
:project
,
to: :schedule
def
shift_duration
# As length_unit is an enum, input is guaranteed to be appropriate
length
.
public_send
(
length_unit
)
# rubocop:disable GitlabSecurity/PublicSend
end
end
end
ee/lib/incident_management/oncall_shift_generator.rb
0 → 100644
View file @
b13eb238
# frozen_string_literal: true
module
IncidentManagement
class
OncallShiftGenerator
# @param rotation [IncidentManagement::OncallRotation]
def
initialize
(
rotation
)
@rotation
=
rotation
end
# Generates an array of shifts which cover the provided time range.
#
# @param starts_at [ActiveSupport::TimeWithZone]
# @param ends_at [ActiveSupport::TimeWithZone]
# @return [IncidentManagement::OncallShift]
def
for_timeframe
(
starts_at
:,
ends_at
:)
starts_at
=
[
apply_timezone
(
starts_at
),
rotation_starts_at
].
max
ends_at
=
apply_timezone
(
ends_at
)
return
[]
unless
starts_at
<
ends_at
return
[]
unless
rotation
.
participants
.
any?
# The first shift within the timeframe may begin before
# the timeframe. We want to begin generating shifts
# based on the actual start time of the shift.
elapsed_shift_count
=
elapsed_whole_shifts
(
starts_at
)
shift_starts_at
=
shift_start_time
(
elapsed_shift_count
)
shifts
=
[]
while
shift_starts_at
<
ends_at
shifts
<<
shift_for
(
elapsed_shift_count
,
shift_starts_at
)
shift_starts_at
+=
shift_duration
elapsed_shift_count
+=
1
end
shifts
end
# Generates a single shift during which the timestamp occurs.
#
# @param timestamp [ActiveSupport::TimeWithZone]
# @return IncidentManagement::OncallShift
def
for_timestamp
(
timestamp
)
timestamp
=
apply_timezone
(
timestamp
)
return
if
timestamp
<
rotation_starts_at
return
unless
rotation
.
participants
.
any?
elapsed_shift_count
=
elapsed_whole_shifts
(
timestamp
)
shift_starts_at
=
shift_start_time
(
elapsed_shift_count
)
shift_for
(
elapsed_shift_count
,
shift_starts_at
)
end
private
attr_reader
:rotation
delegate
:shift_duration
,
to: :rotation
# Starting time of a shift which covers the timestamp.
# @return [ActiveSupport::TimeWithZone]
def
shift_start_time
(
elapsed_shift_count
)
rotation_starts_at
+
(
elapsed_shift_count
*
shift_duration
)
end
# Total completed shifts passed between rotation start
# time and the provided timestamp.
# @return [Integer]
def
elapsed_whole_shifts
(
timestamp
)
elapsed_duration
=
timestamp
-
rotation_starts_at
unless
rotation
.
hours?
# Changing timezones (like during daylight savings) can
# cause a "day" to have a duration other than 24 hours ("weeks" too).
# Since elapsed_duration is in seconds, we need
# account for this variable day/week length to
# determine how many actual shifts have elapsed.
#
# Ex) If a location with daylight savings sets their
# clocks forward an hour, a 1-day shift will last for
# 23 hours if it occurs over that transition.
#
# If we want to generate a shift which occurs 1 week
# after the timezone change, the real elapsed seconds
# will equal 1 week minus an hour.
#
# Seconds per average week: 2 * 7 * 24 * 60 * 60 = 1209600
# Seconds in zone-shifted week: 1209600 - (60 * 60) = 1206000
#
# If we count in seconds, minutes, or hours, these are different durations.
# If we count in "days" or "weeks", these durations are equivalent.
#
# To determine how many effective days or weeks
# a duration (in seconds) was, we need to normalize
# the duration to fit the definition of a 24-hour day.
# We can do this by diffing the UTC-offsets between the
# start time of the rotation and the relevant timestamp.
# This should account for different hemispheres,
# offsets changes other an 1 hour, and one-off timezone changes.
elapsed_duration
+=
timestamp
.
utc_offset
-
rotation_starts_at
.
utc_offset
end
# Uses #round to account for floating point inconsistencies.
(
elapsed_duration
/
shift_duration
).
round
(
5
).
floor
end
# Returns an UNSAVED shift, as this shift won't necessarily
# be persisted.
# @return [IncidentManagement::OncallShift]
def
shift_for
(
elapsed_shift_count
,
shift_starts_at
)
IncidentManagement
::
OncallShift
.
new
(
rotation:
rotation
,
participant:
participants
[
participant_rank
(
elapsed_shift_count
)],
starts_at:
shift_starts_at
,
ends_at:
shift_starts_at
+
shift_duration
)
end
# Position in an array of participants based on the
# number of shifts which have elasped for the rotation.
# @return [Integer]
def
participant_rank
(
elapsed_shifts_count
)
elapsed_shifts_count
%
participants
.
length
end
def
participants
@participants
||=
rotation
.
participants
.
ordered_asc
end
def
rotation_starts_at
@rotation_starts_at
||=
apply_timezone
(
rotation
.
starts_at
)
end
def
apply_timezone
(
timestamp
)
timestamp
.
in_time_zone
(
rotation
.
schedule
.
timezone
)
end
end
end
ee/spec/lib/incident_management/oncall_shift_generator_spec.rb
0 → 100644
View file @
b13eb238
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
IncidentManagement
::
OncallShiftGenerator
do
let_it_be
(
:rotation_start_time
)
{
Time
.
parse
(
'2020-12-08 00:00:00 UTC'
).
utc
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length:
5
,
length_unit: :days
)
}
let
(
:current_time
)
{
Time
.
parse
(
'2020-12-08 15:00:00 UTC'
).
utc
}
let
(
:shift_length
)
{
rotation
.
shift_duration
}
around
do
|
example
|
travel_to
(
current_time
)
{
example
.
run
}
end
shared_context
'with three participants'
do
let_it_be
(
:participant1
)
{
create
(
:incident_management_oncall_participant
,
:with_developer_access
,
rotation:
rotation
)
}
let_it_be
(
:participant2
)
{
create
(
:incident_management_oncall_participant
,
:with_developer_access
,
rotation:
rotation
)
}
let_it_be
(
:participant3
)
{
create
(
:incident_management_oncall_participant
,
:with_developer_access
,
rotation:
rotation
)
}
end
# Compares generated shifts to expected output.
# params:
# description -> String
# shift_params -> formatted as [[participant identifier(Symbol), start_time(String), end_time(String)]].
# start_time & end_time should include offset/UTC identifier.
# participant identifier should reference the variable name of a participant.
#
# Example) [[:participant2, '2020-12-13 00:00:00 UTC', '2020-12-18 00:00:00 UTC']]
# :participant2 would reference `let(:participant2)`
shared_examples
'unsaved shifts'
do
|
description
,
shift_params
|
it
"returns
#{
description
}
"
do
expect
(
shifts
).
to
all
(
be_a
(
IncidentManagement
::
OncallShift
))
expect
(
shifts
.
length
).
to
eq
(
shift_params
.
length
)
shifts
.
each_with_index
do
|
shift
,
idx
|
expect
(
shift
).
to
have_attributes
(
id:
nil
,
rotation:
rotation
,
participant:
send
(
shift_params
[
idx
][
0
]),
starts_at:
Time
.
zone
.
parse
(
shift_params
[
idx
][
1
]),
ends_at:
Time
.
zone
.
parse
(
shift_params
[
idx
][
2
])
)
end
end
end
# For asserting the response is a singular shift rather
# than an array of shifts
shared_examples
'unsaved shift'
do
|
description
,
shift_params
|
let
(
:shifts
)
{
[
shift
]
}
include_examples
'unsaved shifts'
,
description
,
[
shift_params
]
end
describe
'#for_timeframe'
do
let
(
:starts_at
)
{
Time
.
parse
(
'2020-12-08 02:00:00 UTC'
).
utc
}
let
(
:ends_at
)
{
starts_at
+
(
shift_length
*
2
)
}
subject
(
:shifts
)
{
described_class
.
new
(
rotation
).
for_timeframe
(
starts_at:
starts_at
,
ends_at:
ends_at
)
}
context
'with no participants'
do
it
{
is_expected
.
to
be_empty
}
end
context
'with one participant'
do
let_it_be
(
:participant
)
{
create
(
:incident_management_oncall_participant
,
:with_developer_access
,
rotation:
rotation
)
}
it_behaves_like
'unsaved shifts'
,
'3 shifts of 5 days, all for the same participant'
,
[[
:participant
,
'2020-12-08 00:00:00 UTC'
,
'2020-12-13 00:00:00 UTC'
],
[
:participant
,
'2020-12-13 00:00:00 UTC'
,
'2020-12-18 00:00:00 UTC'
],
[
:participant
,
'2020-12-18 00:00:00 UTC'
,
'2020-12-23 00:00:00 UTC'
]]
end
context
'with many participants'
do
include_context
'with three participants'
context
'when end time is earlier than start time'
do
let
(
:ends_at
)
{
starts_at
-
1
.
hour
}
it
{
is_expected
.
to
be_empty
}
end
context
'when start time is the same time as the rotation start time'
do
let
(
:starts_at
)
{
rotation_start_time
}
it_behaves_like
'unsaved shifts'
,
'2 shifts of 5 days starting with first participant at the rotation start time'
,
[[
:participant1
,
'2020-12-08 00:00:00 UTC'
,
'2020-12-13 00:00:00 UTC'
],
[
:participant2
,
'2020-12-13 00:00:00 UTC'
,
'2020-12-18 00:00:00 UTC'
]]
end
context
'when start time is earlier than the rotation start time'
do
let
(
:starts_at
)
{
1
.
day
.
before
(
rotation_start_time
)
}
it_behaves_like
'unsaved shifts'
,
'2 shifts of 5 days starting with the first participant at the rotation start time'
,
[[
:participant1
,
'2020-12-08 00:00:00 UTC'
,
'2020-12-13 00:00:00 UTC'
],
[
:participant2
,
'2020-12-13 00:00:00 UTC'
,
'2020-12-18 00:00:00 UTC'
]]
end
context
'when start time coincides with a shift change'
do
let
(
:starts_at
)
{
rotation_start_time
+
shift_length
}
it_behaves_like
'unsaved shifts'
,
'2 shifts of 5 days, starting with the second participant and the second shift'
,
[[
:participant2
,
'2020-12-13 00:00:00 UTC'
,
'2020-12-18 00:00:00 UTC'
],
[
:participant3
,
'2020-12-18 00:00:00 UTC'
,
'2020-12-23 00:00:00 UTC'
]]
end
context
'when start time is partway through a shift'
do
let
(
:starts_at
)
{
rotation_start_time
+
(
0.6
*
shift_length
)
}
it_behaves_like
'unsaved shifts'
,
'3 shifts of 5 days staring with the first participant which includes the partially completed shift'
,
[[
:participant1
,
'2020-12-08 00:00:00 UTC'
,
'2020-12-13 00:00:00 UTC'
],
[
:participant2
,
'2020-12-13 00:00:00 UTC'
,
'2020-12-18 00:00:00 UTC'
],
[
:participant3
,
'2020-12-18 00:00:00 UTC'
,
'2020-12-23 00:00:00 UTC'
]]
end
context
'when the rotation has been completed many times over'
do
let
(
:starts_at
)
{
rotation_start_time
+
7
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'3 shifts of 5 days starting with the first participant beginning 7 weeks after rotation start time'
,
[[
:participant1
,
'2021-01-22 00:00:00 UTC'
,
'2021-01-27 00:00:00 UTC'
],
[
:participant2
,
'2021-01-27 00:00:00 UTC'
,
'2021-02-01 00:00:00 UTC'
],
[
:participant3
,
'2021-02-01 00:00:00 UTC'
,
'2021-02-06 00:00:00 UTC'
]]
end
context
'when timeframe covers the rotation many times over'
do
let
(
:ends_at
)
{
starts_at
+
(
shift_length
*
6.8
)
}
it_behaves_like
'unsaved shifts'
,
'7 shifts of 5 days starting with the first participant'
,
[[
:participant1
,
'2020-12-08 00:00:00 UTC'
,
'2020-12-13 00:00:00 UTC'
],
[
:participant2
,
'2020-12-13 00:00:00 UTC'
,
'2020-12-18 00:00:00 UTC'
],
[
:participant3
,
'2020-12-18 00:00:00 UTC'
,
'2020-12-23 00:00:00 UTC'
],
[
:participant1
,
'2020-12-23 00:00:00 UTC'
,
'2020-12-28 00:00:00 UTC'
],
[
:participant2
,
'2020-12-28 00:00:00 UTC'
,
'2021-01-02 00:00:00 UTC'
],
[
:participant3
,
'2021-01-02 00:00:00 UTC'
,
'2021-01-07 00:00:00 UTC'
],
[
:participant1
,
'2021-01-07 00:00:00 UTC'
,
'2021-01-12 00:00:00 UTC'
]]
end
end
context
'in timezones with daylight-savings'
do
context
'with positive UTC offsets'
do
let_it_be
(
:schedule
)
{
create
(
:incident_management_oncall_schedule
,
timezone:
'Pacific/Auckland'
)
}
context
'with rotation in hours'
do
context
'switching to daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2020-09-27'
).
beginning_of_day
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :hours
,
length:
1
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
5
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which start in NZST(+1200) and switch to NZDT(+1300)'
,
[[
:participant1
,
'2020-09-27 00:00:00 +1200'
,
'2020-09-27 01:00:00 +1200'
],
[
:participant2
,
'2020-09-27 01:00:00 +1200'
,
'2020-09-27 02:00:00 +1200'
],
[
:participant3
,
'2020-09-27 03:00:00 +1300'
,
'2020-09-27 04:00:00 +1300'
],
[
:participant1
,
'2020-09-27 04:00:00 +1300'
,
'2020-09-27 05:00:00 +1300'
],
[
:participant2
,
'2020-09-27 05:00:00 +1300'
,
'2020-09-27 06:00:00 +1300'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
4
.
hours
}
let
(
:ends_at
)
{
starts_at
+
3
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which are entirely in NZDT(+1300)'
,
[[
:participant2
,
'2020-09-27 05:00:00 +1300'
,
'2020-09-27 06:00:00 +1300'
],
[
:participant3
,
'2020-09-27 06:00:00 +1300'
,
'2020-09-27 07:00:00 +1300'
],
[
:participant1
,
'2020-09-27 07:00:00 +1300'
,
'2020-09-27 08:00:00 +1300'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2021-04-06'
).
beginning_of_day
}
let
(
:ends_at
)
{
starts_at
+
3
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which are entirely back in NZST(+1200) after 2 timezone switches since the rotation start time'
,
[[
:participant1
,
'2021-04-06 00:00:00 +1200'
,
'2021-04-06 01:00:00 +1200'
],
[
:participant2
,
'2021-04-06 01:00:00 +1200'
,
'2021-04-06 02:00:00 +1200'
],
[
:participant3
,
'2021-04-06 02:00:00 +1200'
,
'2021-04-06 03:00:00 +1200'
]]
end
end
context
'switching off daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2021-04-04'
).
beginning_of_day
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :hours
,
length:
1
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
5
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which start in NZDT(+1300) and switch to NZST(+1200)'
,
[[
:participant1
,
'2021-04-04 00:00:00 +1300'
,
'2021-04-04 01:00:00 +1300'
],
[
:participant2
,
'2021-04-04 01:00:00 +1300'
,
'2021-04-04 02:00:00 +1300'
],
[
:participant3
,
'2021-04-04 02:00:00 +1300'
,
'2021-04-04 02:00:00 +1200'
],
[
:participant1
,
'2021-04-04 02:00:00 +1200'
,
'2021-04-04 03:00:00 +1200'
],
[
:participant2
,
'2021-04-04 03:00:00 +1200'
,
'2021-04-04 04:00:00 +1200'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
4
.
hours
}
let
(
:ends_at
)
{
starts_at
+
3
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which are entirely in NZST(+1200)'
,
[[
:participant2
,
'2021-04-04 03:00:00 +1200'
,
'2021-04-04 04:00:00 +1200'
],
[
:participant3
,
'2021-04-04 04:00:00 +1200'
,
'2021-04-04 05:00:00 +1200'
],
[
:participant1
,
'2021-04-04 05:00:00 +1200'
,
'2021-04-04 06:00:00 +1200'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2021-09-27'
).
beginning_of_day
}
let
(
:ends_at
)
{
starts_at
+
3
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which are entirely back in NZST(+1300) after 2 timezone switches since the rotation start time'
,
[[
:participant1
,
'2021-09-27 00:00:00 +1300'
,
'2021-09-27 01:00:00 +1300'
],
[
:participant2
,
'2021-09-27 01:00:00 +1300'
,
'2021-09-27 02:00:00 +1300'
],
[
:participant3
,
'2021-09-27 02:00:00 +1300'
,
'2021-09-27 03:00:00 +1300'
]]
end
end
end
context
'with rotation in days'
do
context
'switching to daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2020-09-26'
).
beginning_of_day
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :days
,
length:
1
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
4
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which start in NZST(+1200) and switch to NZDT(+1300)'
,
[[
:participant1
,
'2020-09-26 00:00:00 +1200'
,
'2020-09-27 00:00:00 +1200'
],
[
:participant2
,
'2020-09-27 00:00:00 +1200'
,
'2020-09-28 00:00:00 +1300'
],
[
:participant3
,
'2020-09-28 00:00:00 +1300'
,
'2020-09-29 00:00:00 +1300'
],
[
:participant1
,
'2020-09-29 00:00:00 +1300'
,
'2020-09-30 00:00:00 +1300'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
3
.
days
}
let
(
:ends_at
)
{
starts_at
+
3
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which are entirely in NZDT(+1300)'
,
[[
:participant1
,
'2020-09-29 00:00:00 +1300'
,
'2020-09-30 00:00:00 +1300'
],
[
:participant2
,
'2020-09-30 00:00:00 +1300'
,
'2020-10-01 00:00:00 +1300'
],
[
:participant3
,
'2020-10-01 00:00:00 +1300'
,
'2020-10-02 00:00:00 +1300'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2021-04-07'
).
beginning_of_day
}
let
(
:ends_at
)
{
starts_at
+
3
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which are entirely back in NZST(+1200) after 2 timezone switches since the rotation start time'
,
[[
:participant2
,
'2021-04-07 00:00:00 +1200'
,
'2021-04-08 00:00:00 +1200'
],
[
:participant3
,
'2021-04-08 00:00:00 +1200'
,
'2021-04-09 00:00:00 +1200'
],
[
:participant1
,
'2021-04-09 00:00:00 +1200'
,
'2021-04-10 00:00:00 +1200'
]]
end
end
context
'switching off daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2021-04-03'
).
beginning_of_day
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :days
,
length:
1
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
4
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which start in NZDT(+1300) and switch to NZST(+1200)'
,
[[
:participant1
,
'2021-04-03 00:00:00 +1300'
,
'2021-04-04 00:00:00 +1300'
],
[
:participant2
,
'2021-04-04 00:00:00 +1300'
,
'2021-04-05 00:00:00 +1200'
],
[
:participant3
,
'2021-04-05 00:00:00 +1200'
,
'2021-04-06 00:00:00 +1200'
],
[
:participant1
,
'2021-04-06 00:00:00 +1200'
,
'2021-04-07 00:00:00 +1200'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
3
.
days
}
let
(
:ends_at
)
{
starts_at
+
3
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which are entirely in NZST(+1200)'
,
[[
:participant1
,
'2021-04-06 00:00:00 +1200'
,
'2021-04-07 00:00:00 +1200'
],
[
:participant2
,
'2021-04-07 00:00:00 +1200'
,
'2021-04-08 00:00:00 +1200'
],
[
:participant3
,
'2021-04-08 00:00:00 +1200'
,
'2021-04-09 00:00:00 +1200'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2021-09-28'
).
beginning_of_day
}
let
(
:ends_at
)
{
starts_at
+
3
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which are entirely back in NZST(+1300) after 2 timezone switches since the rotation start time'
,
[[
:participant2
,
'2021-09-28 00:00:00 +1300'
,
'2021-09-29 00:00:00 +1300'
],
[
:participant3
,
'2021-09-29 00:00:00 +1300'
,
'2021-09-30 00:00:00 +1300'
],
[
:participant1
,
'2021-09-30 00:00:00 +1300'
,
'2021-10-01 00:00:00 +1300'
]]
end
end
end
context
'with rotation in weeks'
do
context
'switching to daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2020-09-01'
).
at_noon
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :weeks
,
length:
2
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
6
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which start in NZST(+1200) and switch to NZDT(+1300)'
,
[[
:participant1
,
'2020-09-01 12:00:00 +1200'
,
'2020-09-15 12:00:00 +1200'
],
[
:participant2
,
'2020-09-15 12:00:00 +1200'
,
'2020-09-29 12:00:00 +1300'
],
[
:participant3
,
'2020-09-29 12:00:00 +1300'
,
'2020-10-13 12:00:00 +1300'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
4
.
weeks
}
let
(
:ends_at
)
{
starts_at
+
4
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which are entirely in NZDT(+1300)'
,
[[
:participant3
,
'2020-09-29 12:00:00 +1300'
,
'2020-10-13 12:00:00 +1300'
],
[
:participant1
,
'2020-10-13 12:00:00 +1300'
,
'2020-10-27 12:00:00 +1300'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2021-04-18'
).
at_noon
}
let
(
:ends_at
)
{
starts_at
+
5
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which are entirely back in NZST(+1200) after 2 timezone switches since the rotation start time'
,
[[
:participant2
,
'2021-04-13 12:00:00 +1200'
,
'2021-04-27 12:00:00 +1200'
],
[
:participant3
,
'2021-04-27 12:00:00 +1200'
,
'2021-05-11 12:00:00 +1200'
],
[
:participant1
,
'2021-05-11 12:00:00 +1200'
,
'2021-05-25 12:00:00 +1200'
]]
end
end
context
'switching off daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2021-03-21'
).
at_noon
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :weeks
,
length:
2
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
6
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which start in NZDT(+1300) and switch to NZST(+1200)'
,
[[
:participant1
,
'2021-03-21 12:00:00 +1300'
,
'2021-04-04 12:00:00 +1200'
],
[
:participant2
,
'2021-04-04 12:00:00 +1200'
,
'2021-04-18 12:00:00 +1200'
],
[
:participant3
,
'2021-04-18 12:00:00 +1200'
,
'2021-05-02 12:00:00 +1200'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
4
.
weeks
}
let
(
:ends_at
)
{
starts_at
+
4
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which are entirely in NZST(+1200)'
,
[[
:participant3
,
'2021-04-18 12:00:00 +1200'
,
'2021-05-02 12:00:00 +1200'
],
[
:participant1
,
'2021-05-02 12:00:00 +1200'
,
'2021-05-16 12:00:00 +1200'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'Pacific/Auckland'
).
parse
(
'2021-09-30'
).
at_noon
}
let
(
:ends_at
)
{
starts_at
+
5
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which are entirely back in NZST(+1200) after 2 timezone switches since the rotation start time'
,
[[
:participant2
,
'2021-09-19 12:00:00 +1200'
,
'2021-10-03 12:00:00 +1300'
],
[
:participant3
,
'2021-10-03 12:00:00 +1300'
,
'2021-10-17 12:00:00 +1300'
],
[
:participant1
,
'2021-10-17 12:00:00 +1300'
,
'2021-10-31 12:00:00 +1300'
],
[
:participant2
,
'2021-10-31 12:00:00 +1300'
,
'2021-11-14 12:00:00 +1300'
]]
end
end
end
end
context
'with negative UTC offsets'
do
let_it_be
(
:schedule
)
{
create
(
:incident_management_oncall_schedule
,
timezone:
'America/New_York'
)
}
context
'with rotation in hours'
do
context
'switching to daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2021-03-14'
).
beginning_of_day
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :hours
,
length:
1
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
5
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which start in EST(-0500) and switch to EDT(-0400)'
,
[[
:participant1
,
'2021-03-14 00:00:00 -0500'
,
'2021-03-14 01:00:00 -0500'
],
[
:participant2
,
'2021-03-14 01:00:00 -0500'
,
'2021-03-14 02:00:00 -0500'
],
[
:participant3
,
'2021-03-14 03:00:00 -0400'
,
'2021-03-14 04:00:00 -0400'
],
[
:participant1
,
'2021-03-14 04:00:00 -0400'
,
'2021-03-14 05:00:00 -0400'
],
[
:participant2
,
'2021-03-14 05:00:00 -0400'
,
'2021-03-14 06:00:00 -0400'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
4
.
hours
}
let
(
:ends_at
)
{
starts_at
+
3
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which are entirely in EDT(-0400)'
,
[[
:participant2
,
'2021-03-14 05:00:00 -0400'
,
'2021-03-14 06:00:00 -0400'
],
[
:participant3
,
'2021-03-14 06:00:00 -0400'
,
'2021-03-14 07:00:00 -0400'
],
[
:participant1
,
'2021-03-14 07:00:00 -0400'
,
'2021-03-14 08:00:00 -0400'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2021-11-08'
).
beginning_of_day
}
let
(
:ends_at
)
{
starts_at
+
3
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which are entirely back in EST(-0500) after 2 timezone switches since the rotation start time'
,
[[
:participant1
,
'2021-11-08 00:00:00 -0500'
,
'2021-11-08 01:00:00 -0500'
],
[
:participant2
,
'2021-11-08 01:00:00 -0500'
,
'2021-11-08 02:00:00 -0500'
],
[
:participant3
,
'2021-11-08 02:00:00 -0500'
,
'2021-11-08 03:00:00 -0500'
]]
end
end
context
'switching off daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2021-11-07'
).
beginning_of_day
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :hours
,
length:
1
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
5
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which start in EDT(-0400) and switch to EST(-0500)'
,
[[
:participant1
,
'2021-11-07 00:00:00 -0400'
,
'2021-11-07 01:00:00 -0400'
],
[
:participant2
,
'2021-11-07 01:00:00 -0400'
,
'2021-11-07 02:00:00 -0400'
],
[
:participant3
,
'2021-11-07 02:00:00 -0400'
,
'2021-11-07 02:00:00 -0500'
],
[
:participant1
,
'2021-11-07 02:00:00 -0500'
,
'2021-11-07 03:00:00 -0500'
],
[
:participant2
,
'2021-11-07 03:00:00 -0500'
,
'2021-11-07 04:00:00 -0500'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
4
.
hours
}
let
(
:ends_at
)
{
starts_at
+
3
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which are entirely in EST(-0500)'
,
[[
:participant2
,
'2021-11-07 03:00:00 -0500'
,
'2021-11-07 04:00:00 -0500'
],
[
:participant3
,
'2021-11-07 04:00:00 -0500'
,
'2021-11-07 05:00:00 -0500'
],
[
:participant1
,
'2021-11-07 05:00:00 -0500'
,
'2021-11-07 06:00:00 -0500'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2022-03-14'
).
beginning_of_day
}
let
(
:ends_at
)
{
starts_at
+
3
.
hours
}
it_behaves_like
'unsaved shifts'
,
'hour-long shifts which are entirely back in EDT(-0400) after 2 timezone switches since the rotation start time'
,
[[
:participant1
,
'2022-03-14 00:00:00 -0400'
,
'2022-03-14 01:00:00 -0400'
],
[
:participant2
,
'2022-03-14 01:00:00 -0400'
,
'2022-03-14 02:00:00 -0400'
],
[
:participant3
,
'2022-03-14 02:00:00 -0400'
,
'2022-03-14 03:00:00 -0400'
]]
end
end
end
context
'with rotation in days'
do
context
'switching to daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2021-03-13'
).
beginning_of_day
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :days
,
length:
1
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
4
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which start in EST(-0500) and switch to EDT(-0400)'
,
[[
:participant1
,
'2021-03-13 00:00:00 -0500'
,
'2021-03-14 00:00:00 -0500'
],
[
:participant2
,
'2021-03-14 00:00:00 -0500'
,
'2021-03-15 00:00:00 -0400'
],
[
:participant3
,
'2021-03-15 00:00:00 -0400'
,
'2021-03-16 00:00:00 -0400'
],
[
:participant1
,
'2021-03-16 00:00:00 -0400'
,
'2021-03-17 00:00:00 -0400'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
3
.
days
}
let
(
:ends_at
)
{
starts_at
+
3
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which are entirely in EDT(-0400)'
,
[[
:participant1
,
'2021-03-16 00:00:00 -0400'
,
'2021-03-17 00:00:00 -0400'
],
[
:participant2
,
'2021-03-17 00:00:00 -0400'
,
'2021-03-18 00:00:00 -0400'
],
[
:participant3
,
'2021-03-18 00:00:00 -0400'
,
'2021-03-19 00:00:00 -0400'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2021-11-10'
).
beginning_of_day
}
let
(
:ends_at
)
{
starts_at
+
3
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which are entirely back in EST(-0500) after 2 timezone switches since the rotation start time'
,
[[
:participant3
,
'2021-11-10 00:00:00 -0500'
,
'2021-11-11 00:00:00 -0500'
],
[
:participant1
,
'2021-11-11 00:00:00 -0500'
,
'2021-11-12 00:00:00 -0500'
],
[
:participant2
,
'2021-11-12 00:00:00 -0500'
,
'2021-11-13 00:00:00 -0500'
]]
end
end
context
'switching off daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2021-11-06'
).
beginning_of_day
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :days
,
length:
1
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
4
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which start in EDT(-0400) and switch to EST(-0500)'
,
[[
:participant1
,
'2021-11-06 00:00:00 -0400'
,
'2021-11-07 00:00:00 -0400'
],
[
:participant2
,
'2021-11-07 00:00:00 -0400'
,
'2021-11-08 00:00:00 -0500'
],
[
:participant3
,
'2021-11-08 00:00:00 -0500'
,
'2021-11-09 00:00:00 -0500'
],
[
:participant1
,
'2021-11-09 00:00:00 -0500'
,
'2021-11-10 00:00:00 -0500'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
3
.
days
}
let
(
:ends_at
)
{
starts_at
+
3
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which are entirely in EST(-0500)'
,
[[
:participant1
,
'2021-11-09 00:00:00 -0500'
,
'2021-11-10 00:00:00 -0500'
],
[
:participant2
,
'2021-11-10 00:00:00 -0500'
,
'2021-11-11 00:00:00 -0500'
],
[
:participant3
,
'2021-11-11 00:00:00 -0500'
,
'2021-11-12 00:00:00 -0500'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2022-03-15'
).
beginning_of_day
}
let
(
:ends_at
)
{
starts_at
+
3
.
days
}
it_behaves_like
'unsaved shifts'
,
'day-long shifts which are entirely back in EDT(-0400) after 2 timezone switches since the rotation start time'
,
[[
:participant1
,
'2022-03-15 00:00:00 -0400'
,
'2022-03-16 00:00:00 -0400'
],
[
:participant2
,
'2022-03-16 00:00:00 -0400'
,
'2022-03-17 00:00:00 -0400'
],
[
:participant3
,
'2022-03-17 00:00:00 -0400'
,
'2022-03-18 00:00:00 -0400'
]]
end
end
end
context
'with rotation in weeks'
do
context
'switching to daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2021-02-25'
).
at_noon
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :weeks
,
length:
2
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
6
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which start in EST(-0500) and switch to EDT(-0400)'
,
[[
:participant1
,
'2021-02-25 12:00:00 -0500'
,
'2021-03-11 12:00:00 -0500'
],
[
:participant2
,
'2021-03-11 12:00:00 -0500'
,
'2021-03-25 12:00:00 -0400'
],
[
:participant3
,
'2021-03-25 12:00:00 -0400'
,
'2021-04-08 12:00:00 -0400'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
4
.
weeks
}
let
(
:ends_at
)
{
starts_at
+
4
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which are entirely in EDT(-0400)'
,
[[
:participant3
,
'2021-03-25 12:00:00 -0400'
,
'2021-04-08 12:00:00 -0400'
],
[
:participant1
,
'2021-04-08 12:00:00 -0400'
,
'2021-04-22 12:00:00 -0400'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2021-11-23'
).
at_noon
}
let
(
:ends_at
)
{
starts_at
+
5
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which are entirely back in EST(-0500) after 2 timezone switches since the rotation start time'
,
[[
:participant2
,
'2021-11-18 12:00:00 -0500'
,
'2021-12-02 12:00:00 -0500'
],
[
:participant3
,
'2021-12-02 12:00:00 -0500'
,
'2021-12-16 12:00:00 -0500'
],
[
:participant1
,
'2021-12-16 12:00:00 -0500'
,
'2021-12-30 12:00:00 -0500'
]]
end
end
context
'switching off daylight savings time'
do
let_it_be
(
:rotation_start_time
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2021-10-26'
).
at_noon
}
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
starts_at:
rotation_start_time
,
length_unit: :weeks
,
length:
2
,
schedule:
schedule
)
}
include_context
'with three participants'
context
'when overlapping the switch'
do
let
(
:starts_at
)
{
rotation_start_time
}
let
(
:ends_at
)
{
starts_at
+
6
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which start in EDT(-0400) and switch to EST(-0500)'
,
[[
:participant1
,
'2021-10-26 12:00:00 -0400'
,
'2021-11-09 12:00:00 -0500'
],
[
:participant2
,
'2021-11-09 12:00:00 -0500'
,
'2021-11-23 12:00:00 -0500'
],
[
:participant3
,
'2021-11-23 12:00:00 -0500'
,
'2021-12-07 12:00:00 -0500'
]]
end
context
'starting after switch'
do
let
(
:starts_at
)
{
rotation_start_time
+
4
.
weeks
}
let
(
:ends_at
)
{
starts_at
+
4
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which are entirely in EST(-0500)'
,
[[
:participant3
,
'2021-11-23 12:00:00 -0500'
,
'2021-12-07 12:00:00 -0500'
],
[
:participant1
,
'2021-12-07 12:00:00 -0500'
,
'2021-12-21 12:00:00 -0500'
]]
end
context
'starting after multiple switches'
do
let
(
:starts_at
)
{
Time
.
find_zone
(
'America/New_York'
).
parse
(
'2022-03-17'
).
at_noon
}
let
(
:ends_at
)
{
starts_at
+
5
.
weeks
}
it_behaves_like
'unsaved shifts'
,
'2-week-long shifts which are entirely back in EDT(-0400) after 2 timezone switches since the rotation start time'
,
[[
:participant2
,
'2022-03-15 12:00:00 -0400'
,
'2022-03-29 12:00:00 -0400'
],
[
:participant3
,
'2022-03-29 12:00:00 -0400'
,
'2022-04-12 12:00:00 -0400'
],
[
:participant1
,
'2022-04-12 12:00:00 -0400'
,
'2022-04-26 12:00:00 -0400'
]]
end
end
end
end
end
end
describe
'#for_timestamp'
do
let
(
:timestamp
)
{
current_time
}
subject
(
:shift
)
{
described_class
.
new
(
rotation
).
for_timestamp
(
timestamp
)
}
context
'with no participants'
do
it
{
is_expected
.
to
be_nil
}
end
context
'with participants'
do
include_context
'with three participants'
context
'when timestamp is before the rotation start time'
do
let
(
:timestamp
)
{
rotation_start_time
-
10
.
minutes
}
it
{
is_expected
.
to
be_nil
}
end
context
'when timestamp matches the rotation start time'
do
let
(
:timestamp
)
{
rotation_start_time
}
it_behaves_like
'unsaved shift'
,
'shift which starts at the same time as the rotation'
,
[
:participant1
,
'2020-12-08 00:00:00 UTC'
,
'2020-12-13 00:00:00 UTC'
]
end
context
'when timestamp matches a shift start/end time'
do
let
(
:timestamp
)
{
rotation_start_time
+
shift_length
}
it_behaves_like
'unsaved shift'
,
'the next shift of the rotation'
,
[
:participant2
,
'2020-12-13 00:00:00 UTC'
,
'2020-12-18 00:00:00 UTC'
]
end
context
'when timestamp is in the middle of a shift'
do
let
(
:timestamp
)
{
rotation_start_time
+
(
1.6
*
shift_length
)
}
it_behaves_like
'unsaved shift'
,
'the shift during which the timestamp occurs'
,
[
:participant2
,
'2020-12-13 00:00:00 UTC'
,
'2020-12-18 00:00:00 UTC'
]
end
end
end
end
ee/spec/models/incident_management/oncall_participant_spec.rb
View file @
b13eb238
...
...
@@ -14,13 +14,13 @@ RSpec.describe IncidentManagement::OncallParticipant do
rotation
.
project
.
add_developer
(
user
)
end
describe
'
.
associations'
do
describe
'associations'
do
it
{
is_expected
.
to
belong_to
(
:rotation
)
}
it
{
is_expected
.
to
belong_to
(
:user
)
}
it
{
is_expected
.
to
have_many
(
:shifts
)
}
end
describe
'
.
validations'
do
describe
'validations'
do
it
{
is_expected
.
to
validate_presence_of
(
:rotation
)
}
it
{
is_expected
.
to
validate_presence_of
(
:user
)
}
it
{
is_expected
.
to
validate_presence_of
(
:color_weight
)
}
...
...
@@ -38,6 +38,18 @@ RSpec.describe IncidentManagement::OncallParticipant do
end
end
describe
'scopes'
do
describe
'.ordered_asc'
do
let_it_be
(
:participant1
)
{
create
(
:incident_management_oncall_participant
,
:with_developer_access
,
rotation:
rotation
)
}
let_it_be
(
:participant2
)
{
create
(
:incident_management_oncall_participant
,
:with_developer_access
,
rotation:
rotation
)
}
let_it_be
(
:participant3
)
{
create
(
:incident_management_oncall_participant
,
:with_developer_access
,
rotation:
rotation
)
}
subject
{
described_class
.
ordered_asc
}
it
{
is_expected
.
to
eq
([
participant1
,
participant2
,
participant3
])
}
end
end
private
def
remove_user_from_project
(
user
,
project
)
...
...
ee/spec/models/incident_management/oncall_rotation_spec.rb
View file @
b13eb238
...
...
@@ -20,6 +20,7 @@ RSpec.describe IncidentManagement::OncallRotation do
it
{
is_expected
.
to
validate_uniqueness_of
(
:name
).
scoped_to
(
:oncall_schedule_id
)
}
it
{
is_expected
.
to
validate_presence_of
(
:starts_at
)
}
it
{
is_expected
.
to
validate_presence_of
(
:length
)
}
it
{
is_expected
.
to
validate_numericality_of
(
:length
)
}
it
{
is_expected
.
to
validate_presence_of
(
:length_unit
)
}
context
'when the oncall rotation with the same name exists'
do
...
...
@@ -33,4 +34,20 @@ RSpec.describe IncidentManagement::OncallRotation do
end
end
end
describe
'#shift_duration'
do
let_it_be
(
:rotation
)
{
create
(
:incident_management_oncall_rotation
,
schedule:
schedule
,
length:
5
,
length_unit: :days
)
}
subject
{
rotation
.
shift_duration
}
it
{
is_expected
.
to
eq
(
5
.
days
)
}
described_class
.
length_units
.
each_key
do
|
unit
|
context
"with a length unit of
#{
unit
}
"
do
let
(
:rotation
)
{
build
(
:incident_management_oncall_rotation
,
schedule:
schedule
,
length_unit:
unit
)
}
it
{
is_expected
.
to
be_a
(
ActiveSupport
::
Duration
)
}
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment