mirror of
https://github.com/grafana/grafana.git
synced 2025-02-15 18:13:32 -06:00
This commit better defines how we set states in resultNormal, resultAlerting, resultError and resultNoData. It changes the existing code to call methods such as SetAlerting, SetPending, SetNormal, SetError and NoData instead of assigning values to each individual field whenever the state is changed. This should make it easier to understand what fields should be set for which states and avoid cases where states are missing, or have additional unexpected fields.
669 lines
18 KiB
Go
669 lines
18 KiB
Go
package state
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"math"
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/benbjohnson/clock"
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
ptr "github.com/xorcare/pointer"
|
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
"github.com/grafana/grafana/pkg/services/screenshot"
|
|
)
|
|
|
|
func TestSetAlerting(t *testing.T) {
|
|
mock := clock.NewMock()
|
|
tests := []struct {
|
|
name string
|
|
state State
|
|
reason string
|
|
startsAt time.Time
|
|
endsAt time.Time
|
|
expected State
|
|
}{{
|
|
name: "state is set to Alerting",
|
|
reason: "this is a reason",
|
|
startsAt: mock.Now(),
|
|
endsAt: mock.Now().Add(time.Minute),
|
|
expected: State{
|
|
State: eval.Alerting,
|
|
StateReason: "this is a reason",
|
|
StartsAt: mock.Now(),
|
|
EndsAt: mock.Now().Add(time.Minute),
|
|
},
|
|
}, {
|
|
name: "previous state is removed",
|
|
state: State{
|
|
State: eval.Normal,
|
|
StateReason: "this is a reason",
|
|
Error: errors.New("this is an error"),
|
|
},
|
|
startsAt: mock.Now(),
|
|
endsAt: mock.Now().Add(time.Minute),
|
|
expected: State{
|
|
State: eval.Alerting,
|
|
StartsAt: mock.Now(),
|
|
EndsAt: mock.Now().Add(time.Minute),
|
|
},
|
|
}}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
actual := test.state
|
|
actual.SetAlerting(test.reason, test.startsAt, test.endsAt)
|
|
assert.Equal(t, test.expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetPending(t *testing.T) {
|
|
mock := clock.NewMock()
|
|
tests := []struct {
|
|
name string
|
|
state State
|
|
reason string
|
|
startsAt time.Time
|
|
endsAt time.Time
|
|
expected State
|
|
}{{
|
|
name: "state is set to Pending",
|
|
reason: "this is a reason",
|
|
startsAt: mock.Now(),
|
|
endsAt: mock.Now().Add(time.Minute),
|
|
expected: State{
|
|
State: eval.Pending,
|
|
StateReason: "this is a reason",
|
|
StartsAt: mock.Now(),
|
|
EndsAt: mock.Now().Add(time.Minute),
|
|
},
|
|
}, {
|
|
name: "previous state is removed",
|
|
state: State{
|
|
State: eval.Pending,
|
|
StateReason: "this is a reason",
|
|
Error: errors.New("this is an error"),
|
|
},
|
|
startsAt: mock.Now(),
|
|
endsAt: mock.Now().Add(time.Minute),
|
|
expected: State{
|
|
State: eval.Pending,
|
|
StartsAt: mock.Now(),
|
|
EndsAt: mock.Now().Add(time.Minute),
|
|
},
|
|
}}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
actual := test.state
|
|
actual.SetPending(test.reason, test.startsAt, test.endsAt)
|
|
assert.Equal(t, test.expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNormal(t *testing.T) {
|
|
mock := clock.NewMock()
|
|
tests := []struct {
|
|
name string
|
|
state State
|
|
reason string
|
|
startsAt time.Time
|
|
endsAt time.Time
|
|
expected State
|
|
}{{
|
|
name: "state is set to Normal",
|
|
reason: "this is a reason",
|
|
startsAt: mock.Now(),
|
|
endsAt: mock.Now().Add(time.Minute),
|
|
expected: State{
|
|
State: eval.Normal,
|
|
StateReason: "this is a reason",
|
|
StartsAt: mock.Now(),
|
|
EndsAt: mock.Now().Add(time.Minute),
|
|
},
|
|
}, {
|
|
name: "previous state is removed",
|
|
state: State{
|
|
State: eval.Normal,
|
|
StateReason: "this is a reason",
|
|
Error: errors.New("this is an error"),
|
|
},
|
|
startsAt: mock.Now(),
|
|
endsAt: mock.Now().Add(time.Minute),
|
|
expected: State{
|
|
State: eval.Normal,
|
|
StartsAt: mock.Now(),
|
|
EndsAt: mock.Now().Add(time.Minute),
|
|
},
|
|
}}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
actual := test.state
|
|
actual.SetNormal(test.reason, test.startsAt, test.endsAt)
|
|
assert.Equal(t, test.expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNoData(t *testing.T) {
|
|
mock := clock.NewMock()
|
|
tests := []struct {
|
|
name string
|
|
state State
|
|
reason string
|
|
startsAt time.Time
|
|
endsAt time.Time
|
|
expected State
|
|
}{{
|
|
name: "state is set to No Data",
|
|
startsAt: mock.Now(),
|
|
endsAt: mock.Now().Add(time.Minute),
|
|
expected: State{
|
|
State: eval.NoData,
|
|
StartsAt: mock.Now(),
|
|
EndsAt: mock.Now().Add(time.Minute),
|
|
},
|
|
}, {
|
|
name: "previous state is removed",
|
|
state: State{
|
|
State: eval.NoData,
|
|
StateReason: "this is a reason",
|
|
Error: errors.New("this is an error"),
|
|
},
|
|
startsAt: mock.Now(),
|
|
endsAt: mock.Now().Add(time.Minute),
|
|
expected: State{
|
|
State: eval.NoData,
|
|
StartsAt: mock.Now(),
|
|
EndsAt: mock.Now().Add(time.Minute),
|
|
},
|
|
}}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
actual := test.state
|
|
actual.SetNoData(test.reason, test.startsAt, test.endsAt)
|
|
assert.Equal(t, test.expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetError(t *testing.T) {
|
|
mock := clock.NewMock()
|
|
tests := []struct {
|
|
name string
|
|
state State
|
|
startsAt time.Time
|
|
endsAt time.Time
|
|
error error
|
|
expected State
|
|
}{{
|
|
name: "state is set to Error",
|
|
startsAt: mock.Now(),
|
|
endsAt: mock.Now().Add(time.Minute),
|
|
error: errors.New("this is an error"),
|
|
expected: State{
|
|
State: eval.Error,
|
|
StateReason: ngmodels.StateReasonError,
|
|
Error: errors.New("this is an error"),
|
|
StartsAt: mock.Now(),
|
|
EndsAt: mock.Now().Add(time.Minute),
|
|
},
|
|
}, {
|
|
name: "previous state is removed",
|
|
state: State{
|
|
State: eval.Error,
|
|
StateReason: "this is a reason",
|
|
Error: errors.New("this is an error"),
|
|
},
|
|
startsAt: mock.Now(),
|
|
endsAt: mock.Now().Add(time.Minute),
|
|
error: errors.New("this is another error"),
|
|
expected: State{
|
|
State: eval.Error,
|
|
StateReason: ngmodels.StateReasonError,
|
|
Error: errors.New("this is another error"),
|
|
StartsAt: mock.Now(),
|
|
EndsAt: mock.Now().Add(time.Minute),
|
|
},
|
|
}}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
actual := test.state
|
|
actual.SetError(test.error, test.startsAt, test.endsAt)
|
|
assert.Equal(t, test.expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMaintain(t *testing.T) {
|
|
mock := clock.NewMock()
|
|
now := mock.Now()
|
|
|
|
// the interval is less than the resend interval of 30 seconds
|
|
s := State{State: eval.Alerting, StartsAt: now, EndsAt: now.Add(time.Second)}
|
|
s.Maintain(10, now.Add(10*time.Second))
|
|
// 10 seconds + 3 x 30 seconds is 100 seconds
|
|
assert.Equal(t, now.Add(100*time.Second), s.EndsAt)
|
|
|
|
// the interval is above the resend interval of 30 seconds
|
|
s = State{State: eval.Alerting, StartsAt: now, EndsAt: now.Add(time.Second)}
|
|
s.Maintain(60, now.Add(10*time.Second))
|
|
// 10 seconds + 3 x 60 seconds is 190 seconds
|
|
assert.Equal(t, now.Add(190*time.Second), s.EndsAt)
|
|
}
|
|
|
|
func TestEnd(t *testing.T) {
|
|
evaluationTime, _ := time.Parse("2006-01-02", "2021-03-25")
|
|
testCases := []struct {
|
|
name string
|
|
expected time.Time
|
|
testRule *ngmodels.AlertRule
|
|
testResult eval.Result
|
|
}{
|
|
{
|
|
name: "less than resend delay: for=unset,interval=10s - endsAt = resendDelay * 3",
|
|
expected: evaluationTime.Add(ResendDelay * 3),
|
|
testRule: &ngmodels.AlertRule{
|
|
IntervalSeconds: 10,
|
|
},
|
|
},
|
|
{
|
|
name: "less than resend delay: for=0s,interval=10s - endsAt = resendDelay * 3",
|
|
expected: evaluationTime.Add(ResendDelay * 3),
|
|
testRule: &ngmodels.AlertRule{
|
|
For: 0 * time.Second,
|
|
IntervalSeconds: 10,
|
|
},
|
|
},
|
|
{
|
|
name: "less than resend delay: for=10s,interval=10s - endsAt = resendDelay * 3",
|
|
expected: evaluationTime.Add(ResendDelay * 3),
|
|
testRule: &ngmodels.AlertRule{
|
|
For: 10 * time.Second,
|
|
IntervalSeconds: 10,
|
|
},
|
|
},
|
|
{
|
|
name: "less than resend delay: for=10s,interval=20s - endsAt = resendDelay * 3",
|
|
expected: evaluationTime.Add(ResendDelay * 3),
|
|
testRule: &ngmodels.AlertRule{
|
|
For: 10 * time.Second,
|
|
IntervalSeconds: 20,
|
|
},
|
|
},
|
|
{
|
|
name: "more than resend delay: for=unset,interval=1m - endsAt = interval * 3",
|
|
expected: evaluationTime.Add(time.Second * 60 * 3),
|
|
testRule: &ngmodels.AlertRule{
|
|
IntervalSeconds: 60,
|
|
},
|
|
},
|
|
{
|
|
name: "more than resend delay: for=0s,interval=1m - endsAt = resendDelay * 3",
|
|
expected: evaluationTime.Add(time.Second * 60 * 3),
|
|
testRule: &ngmodels.AlertRule{
|
|
For: 0 * time.Second,
|
|
IntervalSeconds: 60,
|
|
},
|
|
},
|
|
{
|
|
name: "more than resend delay: for=1m,interval=5m - endsAt = interval * 3",
|
|
expected: evaluationTime.Add(time.Second * 300 * 3),
|
|
testRule: &ngmodels.AlertRule{
|
|
For: time.Minute,
|
|
IntervalSeconds: 300,
|
|
},
|
|
},
|
|
{
|
|
name: "more than resend delay: for=5m,interval=1m - endsAt = interval * 3",
|
|
expected: evaluationTime.Add(time.Second * 60 * 3),
|
|
testRule: &ngmodels.AlertRule{
|
|
For: 300 * time.Second,
|
|
IntervalSeconds: 60,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
r := eval.Result{EvaluatedAt: evaluationTime}
|
|
assert.Equal(t, tc.expected, nextEndsTime(tc.testRule.IntervalSeconds, r.EvaluatedAt))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNeedsSending(t *testing.T) {
|
|
evaluationTime, _ := time.Parse("2006-01-02", "2021-03-25")
|
|
testCases := []struct {
|
|
name string
|
|
resendDelay time.Duration
|
|
expected bool
|
|
testState *State
|
|
}{
|
|
{
|
|
name: "state: alerting and LastSentAt before LastEvaluationTime + ResendDelay",
|
|
resendDelay: 1 * time.Minute,
|
|
expected: true,
|
|
testState: &State{
|
|
State: eval.Alerting,
|
|
LastEvaluationTime: evaluationTime,
|
|
LastSentAt: evaluationTime.Add(-2 * time.Minute),
|
|
},
|
|
},
|
|
{
|
|
name: "state: alerting and LastSentAt after LastEvaluationTime + ResendDelay",
|
|
resendDelay: 1 * time.Minute,
|
|
expected: false,
|
|
testState: &State{
|
|
State: eval.Alerting,
|
|
LastEvaluationTime: evaluationTime,
|
|
LastSentAt: evaluationTime,
|
|
},
|
|
},
|
|
{
|
|
name: "state: alerting and LastSentAt equals LastEvaluationTime + ResendDelay",
|
|
resendDelay: 1 * time.Minute,
|
|
expected: true,
|
|
testState: &State{
|
|
State: eval.Alerting,
|
|
LastEvaluationTime: evaluationTime,
|
|
LastSentAt: evaluationTime.Add(-1 * time.Minute),
|
|
},
|
|
},
|
|
{
|
|
name: "state: pending",
|
|
resendDelay: 1 * time.Minute,
|
|
expected: false,
|
|
testState: &State{
|
|
State: eval.Pending,
|
|
},
|
|
},
|
|
{
|
|
name: "state: alerting and ResendDelay is zero",
|
|
resendDelay: 0 * time.Minute,
|
|
expected: true,
|
|
testState: &State{
|
|
State: eval.Alerting,
|
|
LastEvaluationTime: evaluationTime,
|
|
LastSentAt: evaluationTime,
|
|
},
|
|
},
|
|
{
|
|
name: "state: normal + resolved should send without waiting",
|
|
resendDelay: 1 * time.Minute,
|
|
expected: true,
|
|
testState: &State{
|
|
State: eval.Normal,
|
|
Resolved: true,
|
|
LastEvaluationTime: evaluationTime,
|
|
LastSentAt: evaluationTime,
|
|
},
|
|
},
|
|
{
|
|
name: "state: normal but not resolved does not send after a minute",
|
|
resendDelay: 1 * time.Minute,
|
|
expected: false,
|
|
testState: &State{
|
|
State: eval.Normal,
|
|
Resolved: false,
|
|
LastEvaluationTime: evaluationTime,
|
|
LastSentAt: evaluationTime.Add(-1 * time.Minute),
|
|
},
|
|
},
|
|
{
|
|
name: "state: no-data, needs to be re-sent",
|
|
expected: true,
|
|
resendDelay: 1 * time.Minute,
|
|
testState: &State{
|
|
State: eval.NoData,
|
|
LastEvaluationTime: evaluationTime,
|
|
LastSentAt: evaluationTime.Add(-1 * time.Minute),
|
|
},
|
|
},
|
|
{
|
|
name: "state: no-data, should not be re-sent",
|
|
expected: false,
|
|
resendDelay: 1 * time.Minute,
|
|
testState: &State{
|
|
State: eval.NoData,
|
|
LastEvaluationTime: evaluationTime,
|
|
LastSentAt: evaluationTime.Add(-time.Duration(rand.Int63n(59)+1) * time.Second),
|
|
},
|
|
},
|
|
{
|
|
name: "state: error, needs to be re-sent",
|
|
expected: true,
|
|
resendDelay: 1 * time.Minute,
|
|
testState: &State{
|
|
State: eval.Error,
|
|
LastEvaluationTime: evaluationTime,
|
|
LastSentAt: evaluationTime.Add(-1 * time.Minute),
|
|
},
|
|
},
|
|
{
|
|
name: "state: error, should not be re-sent",
|
|
expected: false,
|
|
resendDelay: 1 * time.Minute,
|
|
testState: &State{
|
|
State: eval.Error,
|
|
LastEvaluationTime: evaluationTime,
|
|
LastSentAt: evaluationTime.Add(-time.Duration(rand.Int63n(59)+1) * time.Second),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert.Equal(t, tc.expected, tc.testState.NeedsSending(tc.resendDelay))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetLastEvaluationValuesForCondition(t *testing.T) {
|
|
genState := func(results []Evaluation) *State {
|
|
return &State{
|
|
Results: results,
|
|
}
|
|
}
|
|
|
|
t.Run("should return nil if no results", func(t *testing.T) {
|
|
result := genState(nil).GetLastEvaluationValuesForCondition()
|
|
require.Nil(t, result)
|
|
})
|
|
t.Run("should return value of the condition of the last result", func(t *testing.T) {
|
|
expected := rand.Float64()
|
|
evals := []Evaluation{
|
|
{
|
|
EvaluationTime: time.Time{},
|
|
EvaluationState: 0,
|
|
Values: map[string]*float64{
|
|
"A": ptr.Float64(rand.Float64()),
|
|
},
|
|
Condition: "A",
|
|
},
|
|
{
|
|
EvaluationTime: time.Time{},
|
|
EvaluationState: 0,
|
|
Values: map[string]*float64{
|
|
"B": ptr.Float64(rand.Float64()),
|
|
"A": ptr.Float64(expected),
|
|
},
|
|
Condition: "A",
|
|
},
|
|
}
|
|
result := genState(evals).GetLastEvaluationValuesForCondition()
|
|
require.Len(t, result, 1)
|
|
require.Contains(t, result, "A")
|
|
require.Equal(t, result["A"], expected)
|
|
})
|
|
t.Run("should return empty map if there is no value for condition", func(t *testing.T) {
|
|
evals := []Evaluation{
|
|
{
|
|
EvaluationTime: time.Time{},
|
|
EvaluationState: 0,
|
|
Values: map[string]*float64{
|
|
"C": ptr.Float64(rand.Float64()),
|
|
},
|
|
Condition: "A",
|
|
},
|
|
}
|
|
result := genState(evals).GetLastEvaluationValuesForCondition()
|
|
require.NotNil(t, result)
|
|
require.Len(t, result, 0)
|
|
})
|
|
t.Run("should use NaN if value is not defined", func(t *testing.T) {
|
|
evals := []Evaluation{
|
|
{
|
|
EvaluationTime: time.Time{},
|
|
EvaluationState: 0,
|
|
Values: map[string]*float64{
|
|
"A": nil,
|
|
},
|
|
Condition: "A",
|
|
},
|
|
}
|
|
result := genState(evals).GetLastEvaluationValuesForCondition()
|
|
require.NotNil(t, result)
|
|
require.Len(t, result, 1)
|
|
require.Contains(t, result, "A")
|
|
require.Truef(t, math.IsNaN(result["A"]), "expected NaN but got %v", result["A"])
|
|
})
|
|
}
|
|
|
|
func TestResolve(t *testing.T) {
|
|
s := State{State: eval.Alerting, EndsAt: time.Now().Add(time.Minute)}
|
|
expected := State{State: eval.Normal, StateReason: "This is a reason", EndsAt: time.Now(), Resolved: true}
|
|
s.Resolve("This is a reason", expected.EndsAt)
|
|
assert.Equal(t, expected, s)
|
|
}
|
|
|
|
func TestShouldTakeImage(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
state eval.State
|
|
previousState eval.State
|
|
previousImage *ngmodels.Image
|
|
resolved bool
|
|
expected bool
|
|
}{{
|
|
name: "should take image for state that just transitioned to alerting",
|
|
state: eval.Alerting,
|
|
previousState: eval.Pending,
|
|
expected: true,
|
|
}, {
|
|
name: "should take image for alerting state without image",
|
|
state: eval.Alerting,
|
|
previousState: eval.Alerting,
|
|
expected: true,
|
|
}, {
|
|
name: "should take image for resolved state",
|
|
state: eval.Normal,
|
|
previousState: eval.Alerting,
|
|
resolved: true,
|
|
expected: true,
|
|
}, {
|
|
name: "should not take image for normal state",
|
|
state: eval.Normal,
|
|
previousState: eval.Normal,
|
|
}, {
|
|
name: "should not take image for pending state",
|
|
state: eval.Pending,
|
|
previousState: eval.Normal,
|
|
}, {
|
|
name: "should not take image for alerting state with image",
|
|
state: eval.Alerting,
|
|
previousState: eval.Alerting,
|
|
previousImage: &ngmodels.Image{Path: "foo.png", URL: "https://example.com/foo.png"},
|
|
}}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
assert.Equal(t, test.expected, shouldTakeImage(test.state, test.previousState, test.previousImage, test.resolved))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTakeImage(t *testing.T) {
|
|
t.Run("ErrNoDashboard should return nil", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
ctx := context.Background()
|
|
r := ngmodels.AlertRule{}
|
|
s := NewMockImageCapturer(ctrl)
|
|
|
|
s.EXPECT().NewImage(ctx, &r).Return(nil, ngmodels.ErrNoDashboard)
|
|
image, err := takeImage(ctx, s, &r)
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, image)
|
|
})
|
|
|
|
t.Run("ErrNoPanel should return nil", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
ctx := context.Background()
|
|
r := ngmodels.AlertRule{DashboardUID: ptr.String("foo")}
|
|
s := NewMockImageCapturer(ctrl)
|
|
|
|
s.EXPECT().NewImage(ctx, &r).Return(nil, ngmodels.ErrNoPanel)
|
|
image, err := takeImage(ctx, s, &r)
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, image)
|
|
})
|
|
|
|
t.Run("ErrScreenshotsUnavailable should return nil", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
ctx := context.Background()
|
|
r := ngmodels.AlertRule{DashboardUID: ptr.String("foo"), PanelID: ptr.Int64(1)}
|
|
s := NewMockImageCapturer(ctrl)
|
|
|
|
s.EXPECT().NewImage(ctx, &r).Return(nil, screenshot.ErrScreenshotsUnavailable)
|
|
image, err := takeImage(ctx, s, &r)
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, image)
|
|
})
|
|
|
|
t.Run("other errors should be returned", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
ctx := context.Background()
|
|
r := ngmodels.AlertRule{DashboardUID: ptr.String("foo"), PanelID: ptr.Int64(1)}
|
|
s := NewMockImageCapturer(ctrl)
|
|
|
|
s.EXPECT().NewImage(ctx, &r).Return(nil, errors.New("unknown error"))
|
|
image, err := takeImage(ctx, s, &r)
|
|
assert.EqualError(t, err, "unknown error")
|
|
assert.Nil(t, image)
|
|
})
|
|
|
|
t.Run("image should be returned", func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
ctx := context.Background()
|
|
r := ngmodels.AlertRule{DashboardUID: ptr.String("foo"), PanelID: ptr.Int64(1)}
|
|
s := NewMockImageCapturer(ctrl)
|
|
|
|
s.EXPECT().NewImage(ctx, &r).Return(&ngmodels.Image{Path: "foo.png"}, nil)
|
|
image, err := takeImage(ctx, s, &r)
|
|
assert.NoError(t, err)
|
|
require.NotNil(t, image)
|
|
assert.Equal(t, ngmodels.Image{Path: "foo.png"}, *image)
|
|
})
|
|
}
|