mirror of
https://github.com/retailcrm/mg-transport-core.git
synced 2024-11-24 22:26:04 +03:00
test coverage
This commit is contained in:
parent
c09319ce8c
commit
6d1ce327e7
122
core/healthcheck/counter_test.go
Normal file
122
core/healthcheck/counter_test.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AtomicCounterTest struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAtomicCounter(t *testing.T) {
|
||||||
|
suite.Run(t, new(AtomicCounterTest))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomicCounterTest) new() Counter {
|
||||||
|
return NewAtomicCounter("test")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomicCounterTest) Test_Name() {
|
||||||
|
t.Assert().Equal("test", t.new().Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomicCounterTest) Test_SetName() {
|
||||||
|
c := t.new()
|
||||||
|
c.SetName("new")
|
||||||
|
t.Assert().Equal("new", c.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomicCounterTest) Test_HitSuccess() {
|
||||||
|
c := t.new()
|
||||||
|
c.HitSuccess()
|
||||||
|
t.Assert().Equal(uint32(1), c.TotalSucceeded())
|
||||||
|
|
||||||
|
c.Failed("test")
|
||||||
|
c.FailureProcessed()
|
||||||
|
c.HitSuccess()
|
||||||
|
t.Assert().Equal(uint32(2), c.TotalSucceeded())
|
||||||
|
t.Assert().False(c.IsFailed())
|
||||||
|
t.Assert().False(c.IsFailureProcessed())
|
||||||
|
t.Assert().Equal("", c.Message())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomicCounterTest) Test_HitFailure() {
|
||||||
|
c := t.new()
|
||||||
|
c.HitFailure()
|
||||||
|
t.Assert().Equal(uint32(1), c.TotalFailed())
|
||||||
|
c.HitFailure()
|
||||||
|
t.Assert().Equal(uint32(2), c.TotalFailed())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomicCounterTest) Test_Failed() {
|
||||||
|
c := t.new()
|
||||||
|
t.Require().False(c.IsFailed())
|
||||||
|
t.Require().Equal("", c.Message())
|
||||||
|
|
||||||
|
c.Failed("message")
|
||||||
|
t.Assert().True(c.IsFailed())
|
||||||
|
t.Assert().Equal("message", c.Message())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomicCounterTest) Test_CountersProcessed() {
|
||||||
|
c := t.new()
|
||||||
|
t.Require().False(c.IsCountersProcessed())
|
||||||
|
|
||||||
|
c.CountersProcessed()
|
||||||
|
t.Assert().True(c.IsCountersProcessed())
|
||||||
|
|
||||||
|
c.ClearCountersProcessed()
|
||||||
|
t.Assert().False(c.IsCountersProcessed())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomicCounterTest) Test_FlushCounters() {
|
||||||
|
c := t.new()
|
||||||
|
c.HitSuccess()
|
||||||
|
t.Require().Equal(uint32(1), c.TotalSucceeded())
|
||||||
|
|
||||||
|
c.FlushCounters()
|
||||||
|
t.Assert().Equal(uint32(1), c.TotalSucceeded())
|
||||||
|
|
||||||
|
c.(*AtomicCounter).timestamp.Store(time.Now().Add(-(DefaultResetPeriod + time.Second)))
|
||||||
|
c.FlushCounters()
|
||||||
|
t.Assert().Equal(uint32(0), c.TotalSucceeded())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomicCounterTest) Test_Concurrency() {
|
||||||
|
c := t.new()
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
c.HitSuccess()
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < 500; i++ {
|
||||||
|
// this delay will ensure that failure is being called after success.
|
||||||
|
// technically, both have been executed concurrently because first 399 calls will not be delayed.
|
||||||
|
if i > 399 {
|
||||||
|
time.Sleep(time.Microsecond)
|
||||||
|
}
|
||||||
|
if i > 400 {
|
||||||
|
c.Failed("total failure")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.HitFailure()
|
||||||
|
}
|
||||||
|
c.FailureProcessed()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
t.Assert().Equal(uint32(1000), c.TotalSucceeded())
|
||||||
|
t.Assert().Equal(uint32(401), c.TotalFailed())
|
||||||
|
t.Assert().True(c.IsFailed())
|
||||||
|
t.Assert().True(c.IsFailureProcessed())
|
||||||
|
t.Assert().Equal("total failure", c.Message())
|
||||||
|
}
|
65
core/healthcheck/notifier_test.go
Normal file
65
core/healthcheck/notifier_test.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
retailcrm "github.com/retailcrm/api-client-go/v2"
|
||||||
|
"github.com/retailcrm/mg-transport-core/v2/core/util/testutil"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"gopkg.in/h2non/gock.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDefaultNotifyFunc(t *testing.T) {
|
||||||
|
apiURL := "https://test.retailcrm.pro"
|
||||||
|
apiKey := "key"
|
||||||
|
msg := "Notification"
|
||||||
|
|
||||||
|
data, err := json.Marshal(retailcrm.NotificationsSendRequest{
|
||||||
|
UserGroups: []retailcrm.UserGroupType{retailcrm.UserGroupSuperadmins},
|
||||||
|
Type: retailcrm.NotificationTypeError,
|
||||||
|
Message: msg,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer gock.Off()
|
||||||
|
gock.New(apiURL).
|
||||||
|
Post("/api/v5/notifications/send").
|
||||||
|
BodyString(url.Values{"notification": {string(data)}}.Encode()).
|
||||||
|
Reply(http.StatusOK).
|
||||||
|
JSON(retailcrm.SuccessfulResponse{Success: true})
|
||||||
|
|
||||||
|
assert.NoError(t, DefaultNotifyFunc(apiURL, apiKey, msg))
|
||||||
|
testutil.AssertNoUnmatchedRequests(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultNotifyFunc_Error(t *testing.T) {
|
||||||
|
apiURL := "https://test.retailcrm.pro"
|
||||||
|
apiKey := "key"
|
||||||
|
msg := "Notification"
|
||||||
|
|
||||||
|
data, err := json.Marshal(retailcrm.NotificationsSendRequest{
|
||||||
|
UserGroups: []retailcrm.UserGroupType{retailcrm.UserGroupSuperadmins},
|
||||||
|
Type: retailcrm.NotificationTypeError,
|
||||||
|
Message: msg,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer gock.Off()
|
||||||
|
gock.New(apiURL).
|
||||||
|
Post("/api/v5/notifications/send").
|
||||||
|
BodyString(url.Values{"notification": {string(data)}}.Encode()).
|
||||||
|
Reply(http.StatusForbidden).
|
||||||
|
JSON(retailcrm.ErrorResponse{
|
||||||
|
SuccessfulResponse: retailcrm.SuccessfulResponse{Success: false},
|
||||||
|
ErrorMessage: "Forbidden",
|
||||||
|
})
|
||||||
|
|
||||||
|
err = DefaultNotifyFunc(apiURL, apiKey, msg)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, "Forbidden", err.Error())
|
||||||
|
testutil.AssertNoUnmatchedRequests(t)
|
||||||
|
}
|
@ -1,13 +1,9 @@
|
|||||||
package healthcheck
|
package healthcheck
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/retailcrm/mg-transport-core/v2/core/logger"
|
"github.com/retailcrm/mg-transport-core/v2/core/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNoConnection = errors.New("no connection")
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// DefaultMinRequests is a default minimal threshold of total requests. If Counter has less than this amount of requests
|
// DefaultMinRequests is a default minimal threshold of total requests. If Counter has less than this amount of requests
|
||||||
// total, it will be skipped because it can trigger false alerts otherwise.
|
// total, it will be skipped because it can trigger false alerts otherwise.
|
||||||
|
363
core/healthcheck/processor_test.go
Normal file
363
core/healthcheck/processor_test.go
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/retailcrm/mg-transport-core/v2/core/util/testutil"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CounterProcessorTest struct {
|
||||||
|
suite.Suite
|
||||||
|
apiURL string
|
||||||
|
apiKey string
|
||||||
|
lang string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounterProcessor(t *testing.T) {
|
||||||
|
suite.Run(t, new(CounterProcessorTest))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) SetupSuite() {
|
||||||
|
t.apiURL = "https://test.retailcrm.pro"
|
||||||
|
t.apiKey = "key"
|
||||||
|
t.lang = "en"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) localizer() NotifyMessageLocalizer {
|
||||||
|
loc := &localizerMock{}
|
||||||
|
loc.On("SetLocale", mock.AnythingOfType("string")).Return()
|
||||||
|
loc.On("GetLocalizedTemplateMessage",
|
||||||
|
mock.AnythingOfType("string"), mock.Anything).Return(
|
||||||
|
func(msg string, tpl map[string]interface{}) string {
|
||||||
|
data, err := json.Marshal(tpl)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s [%s]", msg, string(data))
|
||||||
|
})
|
||||||
|
return loc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) new(
|
||||||
|
nf NotifyFunc, pr ConnectionDataProvider, noLocalizer ...bool) (Processor, testutil.BufferedLogger) {
|
||||||
|
loc := t.localizer()
|
||||||
|
if len(noLocalizer) > 0 && noLocalizer[0] {
|
||||||
|
loc = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log := testutil.NewBufferedLogger()
|
||||||
|
return CounterProcessor{
|
||||||
|
Localizer: loc,
|
||||||
|
Logger: log,
|
||||||
|
Notifier: nf,
|
||||||
|
ConnectionDataProvider: pr,
|
||||||
|
Error: "default error",
|
||||||
|
FailureThreshold: DefaultFailureThreshold,
|
||||||
|
MinRequests: DefaultMinRequests,
|
||||||
|
Debug: true,
|
||||||
|
}, log
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) notifier(err ...error) *notifierMock {
|
||||||
|
if len(err) > 0 && err[0] != nil {
|
||||||
|
return ¬ifierMock{err: err[0]}
|
||||||
|
}
|
||||||
|
return ¬ifierMock{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) provider(notFound ...bool) ConnectionDataProvider {
|
||||||
|
if len(notFound) > 0 && notFound[0] {
|
||||||
|
return func(id int) (apiURL, apiKey, lang string, exists bool) {
|
||||||
|
return "", "", "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func(id int) (apiURL, apiKey, lang string, exists bool) {
|
||||||
|
return t.apiURL, t.apiKey, t.lang, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) counter() mockedCounter {
|
||||||
|
return &counterMock{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) Test_FailureProcessed() {
|
||||||
|
n := t.notifier()
|
||||||
|
p, log := t.new(n.Notify, t.provider())
|
||||||
|
c := t.counter()
|
||||||
|
c.On("IsFailed").Return(true)
|
||||||
|
c.On("IsFailureProcessed").Return(true)
|
||||||
|
|
||||||
|
p.Process(1, c)
|
||||||
|
c.AssertExpectations(t.T())
|
||||||
|
t.Assert().Contains(log.String(), "skipping counter id=1 because its failure is already processed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) Test_CounterFailed_CannotFindConnection() {
|
||||||
|
n := t.notifier()
|
||||||
|
p, log := t.new(n.Notify, t.provider(true))
|
||||||
|
c := t.counter()
|
||||||
|
c.On("IsFailed").Return(true)
|
||||||
|
c.On("IsFailureProcessed").Return(false)
|
||||||
|
|
||||||
|
p.Process(1, c)
|
||||||
|
c.AssertExpectations(t.T())
|
||||||
|
t.Assert().Contains(log.String(), "cannot find connection data for counter id=1")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) Test_CounterFailed_ErrWhileNotifying() {
|
||||||
|
n := t.notifier(errors.New("http status code: 500"))
|
||||||
|
p, log := t.new(n.Notify, t.provider())
|
||||||
|
c := t.counter()
|
||||||
|
c.On("IsFailed").Return(true)
|
||||||
|
c.On("IsFailureProcessed").Return(false)
|
||||||
|
c.On("Message").Return("error message")
|
||||||
|
c.On("FailureProcessed").Return()
|
||||||
|
|
||||||
|
p.Process(1, c)
|
||||||
|
c.AssertExpectations(t.T())
|
||||||
|
t.Assert().Contains(log.String(), "cannot send notification for counter id=1: http status code: 500 (message: error message)")
|
||||||
|
t.Assert().Equal(t.apiURL, n.apiURL)
|
||||||
|
t.Assert().Equal(t.apiKey, n.apiKey)
|
||||||
|
t.Assert().Equal("error message", n.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) Test_CounterFailed_SentNotification() {
|
||||||
|
n := t.notifier()
|
||||||
|
p, log := t.new(n.Notify, t.provider())
|
||||||
|
c := t.counter()
|
||||||
|
c.On("IsFailed").Return(true)
|
||||||
|
c.On("IsFailureProcessed").Return(false)
|
||||||
|
c.On("Message").Return("error message")
|
||||||
|
c.On("FailureProcessed").Return()
|
||||||
|
|
||||||
|
p.Process(1, c)
|
||||||
|
c.AssertExpectations(t.T())
|
||||||
|
t.Assert().Empty(log.String())
|
||||||
|
t.Assert().Equal(t.apiURL, n.apiURL)
|
||||||
|
t.Assert().Equal(t.apiKey, n.apiKey)
|
||||||
|
t.Assert().Equal("error message", n.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) Test_TooFewRequests() {
|
||||||
|
n := t.notifier()
|
||||||
|
p, log := t.new(n.Notify, t.provider())
|
||||||
|
c := t.counter()
|
||||||
|
c.On("IsFailed").Return(false)
|
||||||
|
c.On("TotalFailed").Return(uint32(0))
|
||||||
|
c.On("TotalSucceeded").Return(uint32(DefaultMinRequests - 1))
|
||||||
|
|
||||||
|
p.Process(1, c)
|
||||||
|
c.AssertExpectations(t.T())
|
||||||
|
t.Assert().Contains(log.String(),
|
||||||
|
fmt.Sprintf("skipping counter id=%d because it has fewer than %d requests", 1, DefaultMinRequests))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) Test_ThresholdNotPassed() {
|
||||||
|
n := t.notifier()
|
||||||
|
p, log := t.new(n.Notify, t.provider())
|
||||||
|
c := t.counter()
|
||||||
|
c.On("IsFailed").Return(false)
|
||||||
|
c.On("TotalFailed").Return(uint32(20))
|
||||||
|
c.On("TotalSucceeded").Return(uint32(80))
|
||||||
|
c.On("ClearCountersProcessed").Return()
|
||||||
|
c.On("FlushCounters").Return()
|
||||||
|
|
||||||
|
p.Process(1, c)
|
||||||
|
c.AssertExpectations(t.T())
|
||||||
|
t.Assert().Empty(log.String())
|
||||||
|
t.Assert().Empty(n.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) Test_ThresholdPassed_AlreadyProcessed() {
|
||||||
|
n := t.notifier()
|
||||||
|
p, log := t.new(n.Notify, t.provider())
|
||||||
|
c := t.counter()
|
||||||
|
c.On("IsFailed").Return(false)
|
||||||
|
c.On("TotalFailed").Return(uint32(21))
|
||||||
|
c.On("TotalSucceeded").Return(uint32(79))
|
||||||
|
c.On("IsCountersProcessed").Return(true)
|
||||||
|
|
||||||
|
p.Process(1, c)
|
||||||
|
c.AssertExpectations(t.T())
|
||||||
|
t.Assert().Empty(log.String())
|
||||||
|
t.Assert().Empty(n.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) Test_ThresholdPassed_NoConnectionFound() {
|
||||||
|
n := t.notifier()
|
||||||
|
p, log := t.new(n.Notify, t.provider(true))
|
||||||
|
c := t.counter()
|
||||||
|
c.On("IsFailed").Return(false)
|
||||||
|
c.On("TotalFailed").Return(uint32(21))
|
||||||
|
c.On("TotalSucceeded").Return(uint32(79))
|
||||||
|
c.On("IsCountersProcessed").Return(false)
|
||||||
|
|
||||||
|
p.Process(1, c)
|
||||||
|
c.AssertExpectations(t.T())
|
||||||
|
t.Assert().Contains(log.String(), "cannot find connection data for counter id=1")
|
||||||
|
t.Assert().Empty(n.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) Test_ThresholdPassed_NotifyingError() {
|
||||||
|
n := t.notifier(errors.New("unknown error"))
|
||||||
|
p, log := t.new(n.Notify, t.provider())
|
||||||
|
c := t.counter()
|
||||||
|
c.On("IsFailed").Return(false)
|
||||||
|
c.On("TotalFailed").Return(uint32(21))
|
||||||
|
c.On("TotalSucceeded").Return(uint32(79))
|
||||||
|
c.On("IsCountersProcessed").Return(false)
|
||||||
|
c.On("Name").Return("MockedCounter")
|
||||||
|
c.On("Message").Return("")
|
||||||
|
c.On("CountersProcessed").Return()
|
||||||
|
|
||||||
|
p.Process(1, c)
|
||||||
|
c.AssertExpectations(t.T())
|
||||||
|
t.Assert().Contains(log.String(), "cannot send notification for counter id=1: unknown error (message: )")
|
||||||
|
t.Assert().Equal(`default error [{"Name":"MockedCounter"}]`, n.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) Test_ThresholdPassed_NotificationSent() {
|
||||||
|
n := t.notifier()
|
||||||
|
p, log := t.new(n.Notify, t.provider())
|
||||||
|
c := t.counter()
|
||||||
|
c.On("IsFailed").Return(false)
|
||||||
|
c.On("TotalFailed").Return(uint32(21))
|
||||||
|
c.On("TotalSucceeded").Return(uint32(79))
|
||||||
|
c.On("IsCountersProcessed").Return(false)
|
||||||
|
c.On("Name").Return("MockedCounter")
|
||||||
|
c.On("CountersProcessed").Return()
|
||||||
|
|
||||||
|
p.Process(1, c)
|
||||||
|
c.AssertExpectations(t.T())
|
||||||
|
t.Assert().Empty(log.String())
|
||||||
|
t.Assert().Equal(`default error [{"Name":"MockedCounter"}]`, n.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *CounterProcessorTest) Test_ThresholdPassed_NotificationSent_NoLocalizer() {
|
||||||
|
n := t.notifier()
|
||||||
|
p, log := t.new(n.Notify, t.provider(), true)
|
||||||
|
c := t.counter()
|
||||||
|
c.On("IsFailed").Return(false)
|
||||||
|
c.On("TotalFailed").Return(uint32(21))
|
||||||
|
c.On("TotalSucceeded").Return(uint32(79))
|
||||||
|
c.On("IsCountersProcessed").Return(false)
|
||||||
|
c.On("Name").Return("MockedCounter")
|
||||||
|
c.On("CountersProcessed").Return()
|
||||||
|
|
||||||
|
p.Process(1, c)
|
||||||
|
c.AssertExpectations(t.T())
|
||||||
|
t.Assert().Empty(log.String())
|
||||||
|
t.Assert().Equal(`default error`, n.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
type localizerMock struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localizerMock) SetLocale(lang string) {
|
||||||
|
l.Called(lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localizerMock) GetLocalizedTemplateMessage(messageID string, templateData map[string]interface{}) string {
|
||||||
|
args := l.Called(messageID, templateData)
|
||||||
|
if fn, ok := args.Get(0).(func(string, map[string]interface{}) string); ok {
|
||||||
|
return fn(messageID, templateData)
|
||||||
|
}
|
||||||
|
return args.String(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockedCounter interface {
|
||||||
|
Counter
|
||||||
|
On(methodName string, arguments ...interface{}) *mock.Call
|
||||||
|
AssertExpectations(t mock.TestingT) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type counterMock struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) Name() string {
|
||||||
|
args := cm.Called()
|
||||||
|
return args.String(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) SetName(name string) {
|
||||||
|
cm.Called(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) HitSuccess() {
|
||||||
|
cm.Called()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) HitFailure() {
|
||||||
|
cm.Called()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) TotalSucceeded() uint32 {
|
||||||
|
args := cm.Called()
|
||||||
|
return args.Get(0).(uint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) TotalFailed() uint32 {
|
||||||
|
args := cm.Called()
|
||||||
|
return args.Get(0).(uint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) Message() string {
|
||||||
|
args := cm.Called()
|
||||||
|
return args.String(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) IsFailed() bool {
|
||||||
|
args := cm.Called()
|
||||||
|
return args.Bool(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) Failed(message string) {
|
||||||
|
cm.Called(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) IsFailureProcessed() bool {
|
||||||
|
args := cm.Called()
|
||||||
|
return args.Bool(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) IsCountersProcessed() bool {
|
||||||
|
args := cm.Called()
|
||||||
|
return args.Bool(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) FailureProcessed() {
|
||||||
|
cm.Called()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) CountersProcessed() {
|
||||||
|
cm.Called()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) ClearCountersProcessed() {
|
||||||
|
cm.Called()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *counterMock) FlushCounters() {
|
||||||
|
cm.Called()
|
||||||
|
}
|
||||||
|
|
||||||
|
type notifierMock struct {
|
||||||
|
apiURL string
|
||||||
|
apiKey string
|
||||||
|
message string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *notifierMock) Notify(apiURL, apiKey, msg string) error {
|
||||||
|
n.apiURL = apiURL
|
||||||
|
n.apiKey = apiKey
|
||||||
|
n.message = msg
|
||||||
|
return n.err
|
||||||
|
}
|
68
core/healthcheck/storage_test.go
Normal file
68
core/healthcheck/storage_test.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package healthcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SyncMapStorageTest struct {
|
||||||
|
suite.Suite
|
||||||
|
storage Storage
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyncMapStorage(t *testing.T) {
|
||||||
|
suite.Run(t, new(SyncMapStorageTest))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SyncMapStorageTest) SetupSuite() {
|
||||||
|
t.storage = NewSyncMapStorage(NewAtomicCounter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SyncMapStorageTest) Test_Get() {
|
||||||
|
counter := t.storage.Get(1, "Name")
|
||||||
|
t.Assert().NotNil(counter)
|
||||||
|
t.Assert().IsType(&AtomicCounter{}, counter)
|
||||||
|
t.Assert().Equal("Name", counter.Name())
|
||||||
|
|
||||||
|
newCounter := t.storage.Get(1, "New Name")
|
||||||
|
t.Assert().Equal(counter, newCounter)
|
||||||
|
t.Assert().Equal("New Name", newCounter.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SyncMapStorageTest) Test_Process() {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
t.storage.Process(storageCallbackProcessor{callback: func(id int, counter Counter) bool {
|
||||||
|
t.Assert().Equal(1, id)
|
||||||
|
t.Assert().Equal("New Name", counter.Name())
|
||||||
|
wg.Done()
|
||||||
|
return false
|
||||||
|
}})
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SyncMapStorageTest) Test_Remove() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Fail("unexpected panic:", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
t.storage.Remove(0)
|
||||||
|
t.storage.Remove(-1)
|
||||||
|
t.storage.Remove(1)
|
||||||
|
t.storage.Process(storageCallbackProcessor{callback: func(id int, counter Counter) bool {
|
||||||
|
t.Fail("did not expect any items:", id, counter)
|
||||||
|
return false
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
type storageCallbackProcessor struct {
|
||||||
|
callback func(id int, counter Counter) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p storageCallbackProcessor) Process(id int, counter Counter) bool {
|
||||||
|
return p.callback(id, counter)
|
||||||
|
}
|
1
go.mod
1
go.mod
@ -69,6 +69,7 @@ require (
|
|||||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.0 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.6 // indirect
|
github.com/ugorji/go/codec v1.2.6 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect
|
||||||
|
1
go.sum
1
go.sum
@ -385,6 +385,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
Loading…
Reference in New Issue
Block a user