Skip to content

Commit 27c1180

Browse files
committed
Require manually calling Init() for timeperiod entries
Lazily initializing the timeperiod entries comes with problems regarding the error handling and would also result in race conditions if multiple callers use the object at the same time. This commit changes this so that Init() has to be called explicitly first, allowing proper error handling and read-only use of the object later.
1 parent 86d8e47 commit 27c1180

File tree

3 files changed

+38
-10
lines changed

3 files changed

+38
-10
lines changed

internal/recipient/rotations_test.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"github.com/icinga/icinga-go-library/types"
66
"github.com/icinga/icinga-notifications/internal/timeperiod"
77
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
89
"testing"
910
"time"
1011
)
@@ -36,8 +37,7 @@ func Test_rotationResolver_getCurrentRotations(t *testing.T) {
3637
return t
3738
}
3839

39-
var s rotationResolver
40-
s.update([]*Rotation{
40+
rotations := []*Rotation{
4141
// Weekend rotation starting 2024, alternating between contacts contactWeekend2024a and contactWeekend2024b
4242
{
4343
ActualHandoff: types.UnixMilli(parse("2024-01-01")),
@@ -132,7 +132,18 @@ func Test_rotationResolver_getCurrentRotations(t *testing.T) {
132132
},
133133
},
134134
},
135-
})
135+
}
136+
137+
for _, r := range rotations {
138+
for _, m := range r.Members {
139+
for _, e := range m.TimePeriodEntries {
140+
require.NoError(t, e.Init())
141+
}
142+
}
143+
}
144+
145+
var s rotationResolver
146+
s.update(rotations)
136147

137148
for ts := parse("2023-01-01"); ts.Before(parse("2027-01-01")); ts = ts.Add(30 * time.Minute) {
138149
got := s.getContactsAt(ts)

internal/timeperiod/timeperiod.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"github.com/pkg/errors"
77
"github.com/teambition/rrule-go"
88
"go.uber.org/zap/zapcore"
9-
"log"
109
"time"
1110
)
1211

@@ -129,10 +128,11 @@ func (e *Entry) Init() error {
129128
}
130129

131130
// Contains returns whether a point in time t is covered by this entry.
131+
//
132+
// This function may only be called after a successful call to Init().
132133
func (e *Entry) Contains(t time.Time) bool {
133-
err := e.Init()
134-
if err != nil {
135-
log.Printf("Can't initialize entry: %s", err)
134+
if !e.initialized {
135+
panic("timeperiod.Entry: called Contains() before Init()")
136136
}
137137

138138
if t.Before(e.StartTime.Time()) {
@@ -156,10 +156,11 @@ func (e *Entry) Contains(t time.Time) bool {
156156
// NextTransition returns the next recurrence start or end of this entry relative to the given time inclusively.
157157
// This function returns also time.Time's zero value if there is no transition that starts/ends at/after the
158158
// specified time.
159+
//
160+
// This function may only be called after a successful call to Init().
159161
func (e *Entry) NextTransition(t time.Time) time.Time {
160-
err := e.Init()
161-
if err != nil {
162-
log.Printf("Can't initialize entry: %s", err)
162+
if !e.initialized {
163+
panic("timeperiod.Entry: called NextTransition() before Init()")
163164
}
164165

165166
if t.Before(e.StartTime.Time()) {

internal/timeperiod/timeperiod_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/icinga/icinga-go-library/types"
77
"github.com/icinga/icinga-notifications/internal/timeperiod"
88
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
910
"github.com/teambition/rrule-go"
1011
"testing"
1112
"time"
@@ -30,6 +31,8 @@ func TestEntry(t *testing.T) {
3031
},
3132
}
3233

34+
require.NoError(t, e.Init())
35+
3336
t.Run("TimeAtFirstRecurrenceStart", func(t *testing.T) {
3437
assert.True(t, e.Contains(start))
3538
})
@@ -85,6 +88,8 @@ func TestEntry(t *testing.T) {
8588
RRule: sql.NullString{String: "FREQ=DAILY", Valid: true},
8689
}
8790

91+
require.NoError(t, e.Init())
92+
8893
assert.True(t, e.Contains(start))
8994

9095
tz := time.FixedZone("CET", 60*60)
@@ -108,6 +113,8 @@ func TestEntry(t *testing.T) {
108113
RRule: sql.NullString{String: "FREQ=DAILY", Valid: true},
109114
}
110115

116+
require.NoError(t, e.Init())
117+
111118
t.Run("TimeAtFirstRecurrenceStart", func(t *testing.T) {
112119
assert.Equal(t, end, e.NextTransition(start))
113120
})
@@ -137,6 +144,7 @@ func TestEntry(t *testing.T) {
137144
Timezone: berlin,
138145
RRule: sql.NullString{String: "FREQ=DAILY", Valid: true},
139146
}
147+
require.NoError(t, e.Init())
140148

141149
assert.Equal(t, end, e.NextTransition(start), "next transition should match the first recurrence end")
142150

@@ -168,6 +176,10 @@ func TestTimePeriodTransitions(t *testing.T) {
168176
}},
169177
}
170178

179+
for _, e := range p.Entries {
180+
require.NoError(t, e.Init())
181+
}
182+
171183
assert.Equal(t, end, p.NextTransition(start), "next transition should match the interval end")
172184
})
173185

@@ -194,6 +206,10 @@ func TestTimePeriodTransitions(t *testing.T) {
194206
},
195207
}
196208

209+
for _, e := range p.Entries {
210+
require.NoError(t, e.Init())
211+
}
212+
197213
assert.Equal(t, end, p.NextTransition(start), "next transition should match the interval end")
198214

199215
t.Run("TimeAfterFirstIntervalEnd", func(t *testing.T) {

0 commit comments

Comments
 (0)