diff --git a/go.mod b/go.mod index db1dfbcc..7ffe3ef2 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.18 require ( github.com/bits-and-blooms/bloom/v3 v3.3.1 - github.com/splitio/go-toolkit/v5 v5.4.0 - github.com/stretchr/testify v1.9.0 + github.com/splitio/go-toolkit/v5 v5.4.1-0.20250930172659-38274b802d99 + github.com/stretchr/testify v1.11.1 golang.org/x/sync v0.3.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 06b033a3..b5c1d4eb 100644 --- a/go.sum +++ b/go.sum @@ -19,12 +19,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/redis/go-redis/v9 v9.0.4 h1:FC82T+CHJ/Q/PdyLW++GeCO+Ol59Y4T7R4jbgjvktgc= github.com/redis/go-redis/v9 v9.0.4/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= -github.com/splitio/go-toolkit/v5 v5.4.0 h1:g5WFpRhQomnXCmvfsNOWV4s5AuUrWIZ+amM68G8NBKM= -github.com/splitio/go-toolkit/v5 v5.4.0/go.mod h1:xYhUvV1gga9/1029Wbp5pjnR6Cy8nvBpjw99wAbsMko= +github.com/splitio/go-toolkit/v5 v5.4.1-0.20250930172659-38274b802d99 h1:rQo355F9JbdyTMz2X5MU+FeRvkT6rvD1n+GnXdJr33A= +github.com/splitio/go-toolkit/v5 v5.4.1-0.20250930172659-38274b802d99/go.mod h1:SifzysrOVDbzMcOE8zjX02+FG5az4FrR3Us/i5SeStw= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg= github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= diff --git a/healthcheck/mocks/applicationmonitor.go b/healthcheck/mocks/applicationmonitor.go index ba15ee67..a0ec38a4 100644 --- a/healthcheck/mocks/applicationmonitor.go +++ b/healthcheck/mocks/applicationmonitor.go @@ -1,5 +1,11 @@ package mocks +import ( + "github.com/splitio/go-split-commons/v7/healthcheck/application" + + "github.com/stretchr/testify/mock" +) + // MockApplicationMonitor mocked implementation of application monitor type MockApplicationMonitor struct { NotifyEventCall func(counterType int) @@ -15,3 +21,20 @@ func (m MockApplicationMonitor) NotifyEvent(counterType int) { func (m MockApplicationMonitor) Reset(counterType int, value int) { m.ResetCall(counterType, value) } + +// ApplicationMonitorMock is a mock for the ApplicationMonitor interface +type ApplicationMonitorMock struct { + mock.Mock +} + +// NotifyEvent mock +func (m *ApplicationMonitorMock) NotifyEvent(counterType int) { + m.Called(counterType) +} + +// Reset mock +func (m *ApplicationMonitorMock) Reset(counterType int, value int) { + m.Called(counterType, value) +} + +var _ application.MonitorProducerInterface = (*MockApplicationMonitor)(nil) diff --git a/service/api/specs/splitversionfilter_test.go b/service/api/specs/splitversionfilter_test.go index 8cd424b5..43680447 100644 --- a/service/api/specs/splitversionfilter_test.go +++ b/service/api/specs/splitversionfilter_test.go @@ -28,7 +28,7 @@ func TestParseAndValidate(t *testing.T) { } } -func TestsplitVersionFilter(t *testing.T) { +func TestSplitVersionFilter(t *testing.T) { filter := NewSplitVersionFilter() shouldFilter := filter.ShouldFilter(grammar.MatcherTypeBetweenSemver, FLAG_V1_0) if !shouldFilter { diff --git a/service/mocks/split.go b/service/mocks/split.go index b398554c..edf75de4 100644 --- a/service/mocks/split.go +++ b/service/mocks/split.go @@ -3,14 +3,22 @@ package mocks import ( "github.com/splitio/go-split-commons/v7/dtos" "github.com/splitio/go-split-commons/v7/service" + + "github.com/stretchr/testify/mock" ) // MockSplitFetcher mocked implementation of split fetcher type MockSplitFetcher struct { - FetchCall func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) + mock.Mock } // Fetch mock -func (m MockSplitFetcher) Fetch(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - return m.FetchCall(fetchOptions) +func (m *MockSplitFetcher) Fetch(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { + args := m.Called(fetchOptions) + if args.Get(0) == nil { + return nil, args.Error(1) + } + return args.Get(0).(*dtos.SplitChangesDTO), args.Error(1) } + +var _ service.SplitFetcher = (*MockSplitFetcher)(nil) diff --git a/storage/mocks/event.go b/storage/mocks/event.go index 6d24a5cf..c9d6c7e8 100644 --- a/storage/mocks/event.go +++ b/storage/mocks/event.go @@ -40,7 +40,7 @@ func (m MockEventStorage) Push(event dtos.EventDTO, size int) error { // Drop mock func (m MockEventStorage) Drop(size *int64) error { - return m.Drop(size) + return m.DropCall(size) } // PopNRaw mock diff --git a/storage/mocks/impression.go b/storage/mocks/impression.go index 2161f284..691341c3 100644 --- a/storage/mocks/impression.go +++ b/storage/mocks/impression.go @@ -40,7 +40,7 @@ func (m MockImpressionStorage) PopNWithMetadata(n int64) ([]dtos.ImpressionQueue // Drop mock func (m MockImpressionStorage) Drop(size *int64) error { - return m.Drop(size) + return m.DropCall(size) } // PopNRaw mock diff --git a/storage/mocks/rulebasedsegment.go b/storage/mocks/rulebasedsegment.go index 4987c94c..0f36bd63 100644 --- a/storage/mocks/rulebasedsegment.go +++ b/storage/mocks/rulebasedsegment.go @@ -15,7 +15,7 @@ type MockRuleBasedSegmentStorage struct { // ChangeNumber mock func (m *MockRuleBasedSegmentStorage) ChangeNumber() int64 { args := m.Called() - return int64(args.Int(0)) + return args.Get(0).(int64) } // All mock @@ -56,20 +56,18 @@ func (m *MockRuleBasedSegmentStorage) GetRuleBasedSegmentByName(name string) (*d // SetChangeNumber mock func (m *MockRuleBasedSegmentStorage) SetChangeNumber(till int64) error { - m.Called(till) - return nil + args := m.Called(till) + return args.Error(0) } // Update mock func (m *MockRuleBasedSegmentStorage) Update(toAdd []dtos.RuleBasedSegmentDTO, toRemove []dtos.RuleBasedSegmentDTO, till int64) { m.Called(toAdd, toRemove, till) - return } // Clear mock func (m *MockRuleBasedSegmentStorage) Clear() { m.Called() - return } var _ storage.RuleBasedSegmentsStorage = (*MockRuleBasedSegmentStorage)(nil) diff --git a/storage/mocks/split.go b/storage/mocks/split.go index 124f5774..b618f122 100644 --- a/storage/mocks/split.go +++ b/storage/mocks/split.go @@ -2,7 +2,9 @@ package mocks import ( "github.com/splitio/go-split-commons/v7/dtos" + "github.com/splitio/go-split-commons/v7/storage" "github.com/splitio/go-toolkit/v5/datastructures/set" + "github.com/stretchr/testify/mock" ) // MockSplitStorage is a mocked implementation of Split Storage @@ -96,3 +98,117 @@ func (m MockSplitStorage) GetAllFlagSetNames() []string { func (m MockSplitStorage) ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) { m.ReplaceAllCall(toAdd, changeNumber) } + +// SplitStorageMock is a mocked implementation of Split Storage with testify +type SplitStorageMock struct { + mock.Mock +} + +// All mock +func (m *SplitStorageMock) All() []dtos.SplitDTO { + args := m.Called() + return args.Get(0).([]dtos.SplitDTO) +} + +// ChangeNumber mock +func (m *SplitStorageMock) ChangeNumber() (int64, error) { + args := m.Called() + return args.Get(0).(int64), args.Error(1) +} + +// FetchMany mock +func (m *SplitStorageMock) FetchMany(splitNames []string) map[string]*dtos.SplitDTO { + args := m.Called(splitNames) + if args.Get(0) == nil { + return nil + } + return args.Get(0).(map[string]*dtos.SplitDTO) +} + +// KillLocally mock +func (m *SplitStorageMock) KillLocally(splitName string, defaultTreatment string, changeNumber int64) { + m.Called(splitName, defaultTreatment, changeNumber) +} + +// Update mock +func (m *SplitStorageMock) Update(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { + m.Called(toAdd, toRemove, changeNumber) +} + +// Remove mock +func (m *SplitStorageMock) Remove(splitname string) { + m.Called(splitname) +} + +// SegmentNames mock +func (m *SplitStorageMock) SegmentNames() *set.ThreadUnsafeSet { + args := m.Called() + if args.Get(0) == nil { + return nil + } + return args.Get(0).(*set.ThreadUnsafeSet) +} + +// LargeSegmentNames mock +func (m *SplitStorageMock) LargeSegmentNames() *set.ThreadUnsafeSet { + args := m.Called() + if args.Get(0) == nil { + return nil + } + return args.Get(0).(*set.ThreadUnsafeSet) +} + +// SetChangeNumber mock +func (m *SplitStorageMock) SetChangeNumber(changeNumber int64) error { + args := m.Called(changeNumber) + return args.Error(0) +} + +// Split mock +func (m *SplitStorageMock) Split(splitName string) *dtos.SplitDTO { + args := m.Called(splitName) + if args.Get(0) == nil { + return nil + } + return args.Get(0).(*dtos.SplitDTO) +} + +// SplitNames mock +func (m *SplitStorageMock) SplitNames() []string { + args := m.Called() + if args.Get(0) == nil { + return nil + } + return args.Get(0).([]string) +} + +// TrafficTypeExists mock +func (m *SplitStorageMock) TrafficTypeExists(trafficType string) bool { + args := m.Called(trafficType) + return args.Bool(0) +} + +// GetNamesByFLagSets mock +func (m *SplitStorageMock) GetNamesByFlagSets(sets []string) map[string][]string { + args := m.Called(sets) + if args.Get(0) == nil { + return nil + } + return args.Get(0).(map[string][]string) +} + +// GetAllFlagSetNames mock +func (m *SplitStorageMock) GetAllFlagSetNames() []string { + args := m.Called() + if args.Get(0) == nil { + return nil + } + return args.Get(0).([]string) +} + +// ReplaceAll mock +func (m *SplitStorageMock) ReplaceAll(toAdd []dtos.SplitDTO, changeNumber int64) { + m.Called(toAdd, changeNumber) +} + +var _ storage.SplitStorage = (*SplitStorageMock)(nil) diff --git a/synchronizer/local_test.go b/synchronizer/local_test.go index 51de0a51..25d83f82 100644 --- a/synchronizer/local_test.go +++ b/synchronizer/local_test.go @@ -2,7 +2,6 @@ package synchronizer import ( "errors" - "sync/atomic" "testing" "time" @@ -15,7 +14,10 @@ import ( httpMocks "github.com/splitio/go-split-commons/v7/service/mocks" "github.com/splitio/go-split-commons/v7/storage/mocks" "github.com/splitio/go-split-commons/v7/synchronizer/worker/split" + "github.com/splitio/go-toolkit/v5/logging" + + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -29,33 +31,19 @@ var goClientRuleBasedSegmentRules = []string{grammar.MatcherTypeAllKeys, grammar grammar.MatcherTypeInRuleBasedSegment} func TestLocalSyncAllError(t *testing.T) { - var splitFetchCalled int64 - var notifyEventCalled int64 logger := logging.NewLogger(&logging.LoggerOptions{}) - splitAPI := api.SplitAPI{ - SplitFetcher: httpMocks.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&splitFetchCalled, 1) - if fetchOptions.ChangeNumber() != -1 { - t.Error("Wrong changenumber passed") - } - return nil, errors.New("Some") - }, - }, - } - splitMockStorage := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return -1, nil }, - } + splitFetcher := &httpMocks.MockSplitFetcher{} + splitFetcher.On("Fetch", mock.Anything).Return(nil, errors.New("Some")).Once() + splitAPI := api.SplitAPI{SplitFetcher: splitFetcher} + splitMockStorage := &mocks.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(-1), nil) telemetryMockStorage := mocks.MockTelemetryStorage{} - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Once() flagSetFilter := flagsets.NewFlagSetFilter(nil) ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(int64(-1)) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, goClientFeatureFlagsRules, goClientRuleBasedSegmentRules, logger, nil) splitUpdater := split.NewSplitUpdater( @@ -81,131 +69,85 @@ func TestLocalSyncAllError(t *testing.T) { } err := syncForTest.SyncAll() - if err == nil { - t.Error("It should return error") - } - if atomic.LoadInt64(&splitFetchCalled) != 1 { - t.Error("It should be called once") - } - if atomic.LoadInt64(¬ifyEventCalled) != 1 { - t.Errorf("It should be called once. Actual %d", notifyEventCalled) - } + assert.NotNil(t, err) + + splitFetcher.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) } func TestLocalSyncAllOk(t *testing.T) { - var splitFetchCalled int64 mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} mockedSplit2 := dtos.SplitDTO{Name: "split2", Killed: true, Status: "ACTIVE", TrafficTypeName: "two"} logger := logging.NewLogger(&logging.LoggerOptions{}) - splitAPI := api.SplitAPI{ - SplitFetcher: httpMocks.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&splitFetchCalled, 1) - if fetchOptions.ChangeNumber() != -1 { - t.Error("Wrong changenumber passed") - } - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2}, - Since: 3, - Till: 3}, - }, nil - }, - }, - } - splitMockStorage := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return -1, nil }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { - if changeNumber != 3 { - t.Error("Wrong changenumber") - } - if len(toAdd) != 2 { - t.Error("Wrong length of passed splits") - } - }, - } - var notifyEventCalled int64 + splitFetcher := &httpMocks.MockSplitFetcher{} + splitFetcher.On("Fetch", + mock.MatchedBy(func(req *service.FlagRequestParams) bool { + return req.ChangeNumber() == -1 + })).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2}, + Since: 3, + Till: 3}, + }, nil).Once() + splitAPI := api.SplitAPI{SplitFetcher: splitFetcher} + splitMockStorage := &mocks.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(-1), nil) + splitMockStorage.On("Update", []dtos.SplitDTO{mockedSplit1, mockedSplit2}, []dtos.SplitDTO{}, int64(3)).Once() segmentMockStorage := mocks.MockSegmentStorage{} ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(int64(-1)) ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Once().Return(-1) telemetryMockStorage := mocks.MockTelemetryStorage{ RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, RecordSuccessfulSyncCall: func(resource int, when time.Time) {}, } - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Once() largeSegmentStorage := &mocks.MockLargeSegmentStorage{} syncForTest := NewLocal(&LocalConfig{}, &splitAPI, splitMockStorage, segmentMockStorage, largeSegmentStorage, ruleBasedSegmentMockStorage, logger, telemetryMockStorage, appMonitorMock) err := syncForTest.SyncAll() - if err != nil { - t.Error("It should not return error") - } - if splitFetchCalled != 1 { - t.Error("It should be called once") - } - if atomic.LoadInt64(¬ifyEventCalled) != 1 { - t.Errorf("It should be called once. Actual %d", notifyEventCalled) - } + assert.Nil(t, err) + + splitFetcher.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) } func TestLocalPeriodicFetching(t *testing.T) { - var splitFetchCalled int64 mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} mockedSplit2 := dtos.SplitDTO{Name: "split2", Killed: true, Status: "ACTIVE", TrafficTypeName: "two"} logger := logging.NewLogger(&logging.LoggerOptions{}) - splitAPI := api.SplitAPI{ - SplitFetcher: httpMocks.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&splitFetchCalled, 1) - if fetchOptions.ChangeNumber() != -1 { - t.Error("Wrong changenumber passed") - } - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2}, - Since: 3, - Till: 3}, - }, nil - }, - }, - } - splitMockStorage := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return -1, nil }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { - if changeNumber != 3 { - t.Error("Wrong changenumber") - } - if len(toAdd) != 2 { - t.Error("Wrong length of passed splits") - } - }, - } + splitFetcher := &httpMocks.MockSplitFetcher{} + splitFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2}, + Since: 3, + Till: 3}, + }, nil).Once() + splitAPI := api.SplitAPI{SplitFetcher: splitFetcher} + splitMockStorage := &mocks.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(-1), nil) + splitMockStorage.On("Update", []dtos.SplitDTO{mockedSplit1, mockedSplit2}, []dtos.SplitDTO{}, int64(3)).Once() segmentMockStorage := mocks.MockSegmentStorage{} ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(int64(-1)) ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Once().Return(-1) telemetryMockStorage := mocks.MockTelemetryStorage{ RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, RecordSuccessfulSyncCall: func(resource int, when time.Time) {}, } - var notifyEventCalled int64 - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Once() largeSegmentStorage := &mocks.MockLargeSegmentStorage{} syncForTest := NewLocal(&LocalConfig{RefreshEnabled: true, SplitPeriod: 1}, &splitAPI, splitMockStorage, segmentMockStorage, largeSegmentStorage, ruleBasedSegmentMockStorage, logger, telemetryMockStorage, appMonitorMock) syncForTest.StartPeriodicFetching() time.Sleep(time.Millisecond * 1500) - if atomic.LoadInt64(&splitFetchCalled) != 1 { - t.Error("It should be called once", splitFetchCalled) - } syncForTest.StopPeriodicFetching() - if atomic.LoadInt64(¬ifyEventCalled) != 1 { - t.Errorf("It should be called once. Actual %d", notifyEventCalled) - } + + splitFetcher.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) } diff --git a/synchronizer/synchronizer_test.go b/synchronizer/synchronizer_test.go index da4712ea..539470fd 100644 --- a/synchronizer/synchronizer_test.go +++ b/synchronizer/synchronizer_test.go @@ -16,9 +16,11 @@ import ( "github.com/splitio/go-split-commons/v7/service" "github.com/splitio/go-split-commons/v7/service/api" httpMocks "github.com/splitio/go-split-commons/v7/service/mocks" - "github.com/splitio/go-split-commons/v7/storage/inmemory" "github.com/splitio/go-split-commons/v7/storage/mocks" storageMock "github.com/splitio/go-split-commons/v7/storage/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + syncMocks "github.com/splitio/go-split-commons/v7/synchronizer/mocks" "github.com/splitio/go-split-commons/v7/synchronizer/worker/event" "github.com/splitio/go-split-commons/v7/synchronizer/worker/impression" @@ -26,10 +28,10 @@ import ( "github.com/splitio/go-split-commons/v7/synchronizer/worker/split" "github.com/splitio/go-split-commons/v7/tasks" "github.com/splitio/go-split-commons/v7/telemetry" + "github.com/splitio/go-toolkit/v5/common" "github.com/splitio/go-toolkit/v5/datastructures/set" "github.com/splitio/go-toolkit/v5/logging" - "github.com/stretchr/testify/mock" ) func validReqParams(t *testing.T, fetchOptions service.RequestParams) { @@ -43,44 +45,21 @@ func validReqParams(t *testing.T, fetchOptions service.RequestParams) { } } -func createSplitUpdater(splitMockStorage storageMock.MockSplitStorage, splitAPI api.SplitAPI, logger logging.LoggerInterface, telemetryMockStorage storageMock.MockTelemetryStorage, appMonitorMock hcMock.MockApplicationMonitor) split.Updater { - ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Maybe().Return(-1) - ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(-1) - - largeSegmentStorage := &mocks.MockLargeSegmentStorage{} - ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, goClientFeatureFlagsRules, goClientRuleBasedSegmentRules, logger, nil) - splitUpdater := split.NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitAPI.SplitFetcher, logger, telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) - return splitUpdater -} - func TestSyncAllErrorSplits(t *testing.T) { - var splitFetchCalled int64 - var notifyEventCalled int64 logger := logging.NewLogger(&logging.LoggerOptions{}) - splitAPI := api.SplitAPI{ - SplitFetcher: httpMocks.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&splitFetchCalled, 1) - validReqParams(t, fetchOptions) - return nil, errors.New("Some") - }, - }, - } - splitMockStorage := storageMock.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return -1, nil }, - } + splitFetcher := &httpMocks.MockSplitFetcher{} + splitFetcher.On("Fetch", mock.Anything).Return(nil, errors.New("some")) + splitAPI := api.SplitAPI{SplitFetcher: splitFetcher} + splitMockStorage := &storageMock.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(-1), nil).Times(2) telemetryMockStorage := storageMock.MockTelemetryStorage{ RecordSuccessfulSyncCall: func(resource int, time time.Time) {}, } - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Once() advanced := conf.AdvancedConfig{EventsQueueSize: 100, EventsBulkSize: 100, HTTPTimeout: 100, ImpressionsBulkSize: 100, ImpressionsQueueSize: 100, SegmentQueueSize: 50, SegmentWorkers: 5} ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Maybe().Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(-1)).Times(2) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} @@ -103,36 +82,27 @@ func TestSyncAllErrorSplits(t *testing.T) { } syncForTest := NewSynchronizer(advanced, splitTasks, workers, logger, nil) err := syncForTest.SyncAll() - if err == nil { - t.Error("It should return error") - } - if atomic.LoadInt64(&splitFetchCalled) != 1 { - t.Error("It should be called once") - } - if atomic.LoadInt64(¬ifyEventCalled) != 1 { - t.Error("It should be called once") - } + assert.NotNil(t, err) + + splitFetcher.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) } func TestSyncAllErrorInSegments(t *testing.T) { - var splitFetchCalled int64 var segmentFetchCalled int64 - var notifyEventCalled int64 mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} mockedSplit2 := dtos.SplitDTO{Name: "split2", Killed: true, Status: "ACTIVE", TrafficTypeName: "two"} logger := logging.NewLogger(&logging.LoggerOptions{}) + splitFetcher := &httpMocks.MockSplitFetcher{} + splitFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2}, + Since: 3, + Till: 3}, + }, nil) splitAPI := api.SplitAPI{ - SplitFetcher: httpMocks.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&splitFetchCalled, 1) - validReqParams(t, fetchOptions) - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2}, - Since: 3, - Till: 3}, - }, nil - }, - }, + SplitFetcher: splitFetcher, SegmentFetcher: httpMocks.MockSegmentFetcher{ FetchCall: func(name string, fetchOptions *service.SegmentRequestParams) (*dtos.SegmentChangesDTO, error) { atomic.AddInt64(&segmentFetchCalled, 1) @@ -144,18 +114,10 @@ func TestSyncAllErrorInSegments(t *testing.T) { }, }, } - splitMockStorage := storageMock.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return -1, nil }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { - if changeNumber != 3 { - t.Error("Wrong changenumber") - } - if len(toAdd) != 2 { - t.Error("Wrong length of passed splits") - } - }, - SegmentNamesCall: func() *set.ThreadUnsafeSet { return set.NewSet("segment1", "segment2") }, - } + splitMockStorage := &storageMock.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(-1), nil).Times(2) + splitMockStorage.On("Update", mock.Anything, []dtos.SplitDTO{}, int64(3)).Return().Once() + splitMockStorage.On("SegmentNames").Return(set.NewSet("segment1", "segment2")).Once() segmentMockStorage := storageMock.MockSegmentStorage{ ChangeNumberCall: func(segmentName string) (int64, error) { return -1, nil }, } @@ -163,16 +125,18 @@ func TestSyncAllErrorInSegments(t *testing.T) { RecordSuccessfulSyncCall: func(resource int, time time.Time) {}, RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, } - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Times(4) ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} ruleBasedSegmentMockStorage.On("GetSegments").Return(set.NewSet()) + ruleBasedSegmentMockStorage.On("ChangeNumber").Maybe().Return(int64(-1)) + ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(-1) + largeSegmentStorage := &mocks.MockLargeSegmentStorage{} + ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, goClientFeatureFlagsRules, goClientRuleBasedSegmentRules, logger, nil) + splitUpdater := split.NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitAPI.SplitFetcher, logger, telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) advanced := conf.AdvancedConfig{EventsQueueSize: 100, EventsBulkSize: 100, HTTPTimeout: 100, ImpressionsBulkSize: 100, ImpressionsQueueSize: 100, SegmentQueueSize: 50, SegmentWorkers: 5} workers := Workers{ - SplitUpdater: createSplitUpdater(splitMockStorage, splitAPI, logger, telemetryMockStorage, appMonitorMock), + SplitUpdater: splitUpdater, SegmentUpdater: segment.NewSegmentUpdater(splitMockStorage, segmentMockStorage, ruleBasedSegmentMockStorage, splitAPI.SegmentFetcher, logger, telemetryMockStorage, appMonitorMock), EventRecorder: event.NewEventRecorderSingle(storageMock.MockEventStorage{}, splitAPI.EventRecorder, logger, dtos.Metadata{}, telemetryMockStorage), ImpressionRecorder: impression.NewRecorderSingle(storageMock.MockImpressionStorage{}, splitAPI.ImpressionRecorder, logger, dtos.Metadata{}, conf.ImpressionsModeDebug, telemetryMockStorage), @@ -187,39 +151,28 @@ func TestSyncAllErrorInSegments(t *testing.T) { } syncForTest := NewSynchronizer(advanced, splitTasks, workers, logger, nil) err := syncForTest.SyncAll() - if err == nil { - t.Error("It should return error") - } - if atomic.LoadInt64(&splitFetchCalled) != 1 { - t.Error("It should be called once") - } - if atomic.LoadInt64(&segmentFetchCalled) != 2 { - t.Error("It should be called twice") - } - if atomic.LoadInt64(¬ifyEventCalled) < 1 { - t.Error("It should be called at least once") - } + assert.NotNil(t, err) + assert.Equal(t, int64(2), atomic.LoadInt64(&segmentFetchCalled)) + + splitFetcher.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) } func TestSyncAllOk(t *testing.T) { - var splitFetchCalled int64 var segmentFetchCalled int64 - var notifyEventCalled int64 mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} mockedSplit2 := dtos.SplitDTO{Name: "split2", Killed: true, Status: "ACTIVE", TrafficTypeName: "two"} logger := logging.NewLogger(&logging.LoggerOptions{}) + splitFetcher := &httpMocks.MockSplitFetcher{} + splitFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2}, + Since: 3, + Till: 3}, + }, nil) splitAPI := api.SplitAPI{ - SplitFetcher: httpMocks.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&splitFetchCalled, 1) - validReqParams(t, fetchOptions) - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2}, - Since: 3, - Till: 3}, - }, nil - }, - }, + SplitFetcher: splitFetcher, SegmentFetcher: httpMocks.MockSegmentFetcher{ FetchCall: func(name string, fetchOptions *service.SegmentRequestParams) (*dtos.SegmentChangesDTO, error) { atomic.AddInt64(&segmentFetchCalled, 1) @@ -237,18 +190,10 @@ func TestSyncAllOk(t *testing.T) { }, }, } - splitMockStorage := storageMock.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return -1, nil }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { - if changeNumber != 3 { - t.Error("Wrong changenumber") - } - if len(toAdd) != 2 { - t.Error("Wrong length of passed splits") - } - }, - SegmentNamesCall: func() *set.ThreadUnsafeSet { return set.NewSet("segment1", "segment2") }, - } + splitMockStorage := &storageMock.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(-1), nil).Times(2) + splitMockStorage.On("Update", mock.Anything, []dtos.SplitDTO{}, int64(3)).Return().Once() + splitMockStorage.On("SegmentNames").Return(set.NewSet("segment1", "segment2")).Once() segmentMockStorage := storageMock.MockSegmentStorage{ ChangeNumberCall: func(segmentName string) (int64, error) { return -1, nil }, KeysCall: func(segmentName string) *set.ThreadUnsafeSet { @@ -268,17 +213,18 @@ func TestSyncAllOk(t *testing.T) { RecordSuccessfulSyncCall: func(resource int, time time.Time) {}, RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, } - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Times(4) ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} ruleBasedSegmentMockStorage.On("GetSegments").Return(set.NewSet()) - + ruleBasedSegmentMockStorage.On("ChangeNumber").Maybe().Return(int64(-1)) + ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(-1) + largeSegmentStorage := &mocks.MockLargeSegmentStorage{} + ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, goClientFeatureFlagsRules, goClientRuleBasedSegmentRules, logger, nil) + splitUpdater := split.NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitAPI.SplitFetcher, logger, telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) advanced := conf.AdvancedConfig{EventsQueueSize: 100, EventsBulkSize: 100, HTTPTimeout: 100, ImpressionsBulkSize: 100, ImpressionsQueueSize: 100, SegmentQueueSize: 50, SegmentWorkers: 5} workers := Workers{ - SplitUpdater: createSplitUpdater(splitMockStorage, splitAPI, logger, telemetryMockStorage, appMonitorMock), + SplitUpdater: splitUpdater, SegmentUpdater: segment.NewSegmentUpdater(splitMockStorage, segmentMockStorage, ruleBasedSegmentMockStorage, splitAPI.SegmentFetcher, logger, telemetryMockStorage, appMonitorMock), EventRecorder: event.NewEventRecorderSingle(storageMock.MockEventStorage{}, splitAPI.EventRecorder, logger, dtos.Metadata{}, telemetryMockStorage), ImpressionRecorder: impression.NewRecorderSingle(storageMock.MockImpressionStorage{}, splitAPI.ImpressionRecorder, logger, dtos.Metadata{}, conf.ImpressionsModeDebug, telemetryMockStorage), @@ -293,39 +239,27 @@ func TestSyncAllOk(t *testing.T) { } syncForTest := NewSynchronizer(advanced, splitTasks, workers, logger, nil) err := syncForTest.SyncAll() - if err != nil { - t.Error("It should not return error") - } - if splitFetchCalled != 1 { - t.Error("It should be called once") - } - if segmentFetchCalled != 2 { - t.Error("It should be called twice") - } - if atomic.LoadInt64(¬ifyEventCalled) < 1 { - t.Error("It should be called at least once") - } + assert.Nil(t, err) + assert.Equal(t, int64(2), atomic.LoadInt64(&segmentFetchCalled)) + splitFetcher.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) } func TestPeriodicFetching(t *testing.T) { - var splitFetchCalled int64 var segmentFetchCalled int64 - var notifyEventCalled int64 mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} mockedSplit2 := dtos.SplitDTO{Name: "split2", Killed: true, Status: "ACTIVE", TrafficTypeName: "two"} logger := logging.NewLogger(&logging.LoggerOptions{}) + splitFetcher := &httpMocks.MockSplitFetcher{} + splitFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2}, + Since: 3, + Till: 3}, + }, nil).Once() splitAPI := api.SplitAPI{ - SplitFetcher: httpMocks.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&splitFetchCalled, 1) - validReqParams(t, fetchOptions) - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2}, - Since: 3, - Till: 3}, - }, nil - }, - }, + SplitFetcher: splitFetcher, SegmentFetcher: httpMocks.MockSegmentFetcher{ FetchCall: func(name string, fetchOptions *service.SegmentRequestParams) (*dtos.SegmentChangesDTO, error) { atomic.AddInt64(&segmentFetchCalled, 1) @@ -343,18 +277,10 @@ func TestPeriodicFetching(t *testing.T) { }, }, } - splitMockStorage := storageMock.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return -1, nil }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { - if changeNumber != 3 { - t.Error("Wrong changenumber") - } - if len(toAdd) != 2 { - t.Error("Wrong length of passed splits") - } - }, - SegmentNamesCall: func() *set.ThreadUnsafeSet { return set.NewSet("segment1", "segment2") }, - } + splitMockStorage := &storageMock.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(-1), nil) + splitMockStorage.On("Update", mock.Anything, []dtos.SplitDTO{}, int64(3)).Return().Once() + splitMockStorage.On("SegmentNames").Return(set.NewSet("segment1", "segment2")).Twice() segmentMockStorage := storageMock.MockSegmentStorage{ ChangeNumberCall: func(segmentName string) (int64, error) { return -1, nil }, KeysCall: func(segmentName string) *set.ThreadUnsafeSet { @@ -374,40 +300,30 @@ func TestPeriodicFetching(t *testing.T) { RecordSuccessfulSyncCall: func(resource int, time time.Time) {}, RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, } - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - ResetCall: func(counterType, value int) {}, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} + ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(-1)) + largeSegmentStorage := &mocks.MockLargeSegmentStorage{} + ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, goClientFeatureFlagsRules, goClientRuleBasedSegmentRules, logger, nil) + splitUpdater := split.NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitAPI.SplitFetcher, logger, telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) advanced := conf.AdvancedConfig{EventsQueueSize: 100, EventsBulkSize: 100, HTTPTimeout: 100, ImpressionsBulkSize: 100, ImpressionsQueueSize: 100, SegmentQueueSize: 50, SegmentWorkers: 5} workers := Workers{ - SplitUpdater: createSplitUpdater(splitMockStorage, splitAPI, logger, telemetryMockStorage, appMonitorMock), - SegmentUpdater: segment.NewSegmentUpdater(splitMockStorage, segmentMockStorage, ruleBasedSegmentMockStorage, splitAPI.SegmentFetcher, logger, telemetryMockStorage, appMonitorMock), - EventRecorder: event.NewEventRecorderSingle(storageMock.MockEventStorage{}, splitAPI.EventRecorder, logger, dtos.Metadata{}, telemetryMockStorage), - ImpressionRecorder: impression.NewRecorderSingle(storageMock.MockImpressionStorage{}, splitAPI.ImpressionRecorder, logger, dtos.Metadata{}, conf.ImpressionsModeDebug, telemetryMockStorage), - TelemetryRecorder: telemetry.NewTelemetrySynchronizer(telemetryMockStorage, nil, nil, nil, nil, dtos.Metadata{}, telemetryMockStorage), + SplitUpdater: splitUpdater, + SegmentUpdater: segment.NewSegmentUpdater(splitMockStorage, segmentMockStorage, ruleBasedSegmentMockStorage, splitAPI.SegmentFetcher, logger, telemetryMockStorage, appMonitorMock), } splitTasks := SplitTasks{ - EventSyncTask: tasks.NewRecordEventsTask(workers.EventRecorder, advanced.EventsBulkSize, 1, logger), - ImpressionSyncTask: tasks.NewRecordImpressionsTask(workers.ImpressionRecorder, 1, logger, advanced.ImpressionsBulkSize), - SegmentSyncTask: tasks.NewFetchSegmentsTask(workers.SegmentUpdater, 1, advanced.SegmentWorkers, advanced.SegmentQueueSize, logger, appMonitorMock), - SplitSyncTask: tasks.NewFetchSplitsTask(workers.SplitUpdater, 1, logger), - TelemetrySyncTask: tasks.NewRecordTelemetryTask(workers.TelemetryRecorder, 10, logger), + SegmentSyncTask: tasks.NewFetchSegmentsTask(workers.SegmentUpdater, 1, advanced.SegmentWorkers, advanced.SegmentQueueSize, logger, appMonitorMock), + SplitSyncTask: tasks.NewFetchSplitsTask(workers.SplitUpdater, 1, logger), } syncForTest := NewSynchronizer(advanced, splitTasks, workers, logger, nil) syncForTest.StartPeriodicFetching() time.Sleep(time.Millisecond * 2200) - if atomic.LoadInt64(&splitFetchCalled) < 2 { - t.Error("It should be called twice") - } - if atomic.LoadInt64(&segmentFetchCalled) < 2 { - t.Error("It should be called twice") - } - if atomic.LoadInt64(¬ifyEventCalled) < 1 { - t.Error("It should be called at least once") - } + assert.Equal(t, int64(4), atomic.LoadInt64(&segmentFetchCalled)) + splitFetcher.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) syncForTest.StopPeriodicFetching() } @@ -415,24 +331,19 @@ func TestPeriodicRecording(t *testing.T) { var impressionsCalled int64 var eventsCalled int64 var statsCalled int64 - var notifyEventCalled int64 logger := logging.NewLogger(&logging.LoggerOptions{}) splitAPI := api.SplitAPI{ EventRecorder: httpMocks.MockEventRecorder{ RecordCall: func(events []dtos.EventDTO, metadata dtos.Metadata) error { atomic.AddInt64(&eventsCalled, 1) - if len(events) != 1 { - t.Error("Wrong length") - } + assert.Len(t, events, 1) return nil }, }, ImpressionRecorder: httpMocks.MockImpressionRecorder{ RecordCall: func(impressions []dtos.ImpressionsDTO, metadata dtos.Metadata, extraHeaders map[string]string) error { atomic.AddInt64(&impressionsCalled, 1) - if len(impressions) != 1 { - t.Error("Wrong length") - } + assert.Len(t, impressions, 1) return nil }, }, @@ -445,9 +356,7 @@ func TestPeriodicRecording(t *testing.T) { } impressionMockStorage := storageMock.MockImpressionStorage{ PopNCall: func(n int64) ([]dtos.Impression, error) { - if n != 100 { - t.Error("It should be 100") - } + assert.Equal(t, int64(100), n) return []dtos.Impression{{ BucketingKey: "someBucketingKey", ChangeNumber: 123456789, @@ -458,13 +367,11 @@ func TestPeriodicRecording(t *testing.T) { Treatment: "someTreatment", }}, nil }, - EmptyCall: func() bool { return impressionsCalled >= 3 }, + EmptyCall: func() bool { return atomic.LoadInt64(&impressionsCalled) != 1 }, } eventMockStorage := storageMock.MockEventStorage{ PopNCall: func(n int64) ([]dtos.EventDTO, error) { - if n != 100 { - t.Error("It should be 100") - } + assert.Equal(t, int64(100), n) return []dtos.EventDTO{{ EventTypeID: "someEvent", Key: "someKey", @@ -474,7 +381,7 @@ func TestPeriodicRecording(t *testing.T) { Value: nil, }}, nil }, - EmptyCall: func() bool { return eventsCalled >= 4 }, + EmptyCall: func() bool { return atomic.LoadInt64(&eventsCalled) != 1 }, } telemetryMockStorage := storageMock.MockTelemetryStorage{ PopLatenciesCall: func() dtos.MethodLatencies { return dtos.MethodLatencies{} }, @@ -493,23 +400,15 @@ func TestPeriodicRecording(t *testing.T) { RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, PopUpdatesFromSSECall: func() dtos.UpdatesFromSSE { return dtos.UpdatesFromSSE{} }, } - splitMockStorage := storageMock.MockSplitStorage{ - SplitNamesCall: func() []string { return []string{} }, - SegmentNamesCall: func() *set.ThreadUnsafeSet { return set.NewSet() }, - } + splitMockStorage := &storageMock.SplitStorageMock{} + splitMockStorage.On("SplitNames").Return([]string{}) + splitMockStorage.On("SegmentNames").Return(set.NewSet()) segmentMockStorage := storageMock.MockSegmentStorage{ SegmentKeysCountCall: func() int64 { return 30 }, } - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } - ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} + appMonitorMock := &hcMock.ApplicationMonitorMock{} advanced := conf.AdvancedConfig{EventsQueueSize: 100, EventsBulkSize: 100, HTTPTimeout: 100, ImpressionsBulkSize: 100, ImpressionsQueueSize: 100, SegmentQueueSize: 50, SegmentWorkers: 5} workers := Workers{ - SplitUpdater: createSplitUpdater(splitMockStorage, splitAPI, logger, telemetryMockStorage, appMonitorMock), - SegmentUpdater: segment.NewSegmentUpdater(splitMockStorage, segmentMockStorage, ruleBasedSegmentMockStorage, splitAPI.SegmentFetcher, logger, telemetryMockStorage, appMonitorMock), EventRecorder: event.NewEventRecorderSingle(eventMockStorage, splitAPI.EventRecorder, logger, dtos.Metadata{}, telemetryMockStorage), ImpressionRecorder: impression.NewRecorderSingle(impressionMockStorage, splitAPI.ImpressionRecorder, logger, dtos.Metadata{}, conf.ImpressionsModeDebug, telemetryMockStorage), TelemetryRecorder: telemetry.NewTelemetrySynchronizer(telemetryMockStorage, splitAPI.TelemetryRecorder, splitMockStorage, segmentMockStorage, logger, dtos.Metadata{}, telemetryMockStorage), @@ -517,39 +416,64 @@ func TestPeriodicRecording(t *testing.T) { splitTasks := SplitTasks{ EventSyncTask: tasks.NewRecordEventsTask(workers.EventRecorder, advanced.EventsBulkSize, 1, logger), ImpressionSyncTask: tasks.NewRecordImpressionsTask(workers.ImpressionRecorder, 1, logger, advanced.ImpressionsBulkSize), - SegmentSyncTask: tasks.NewFetchSegmentsTask(workers.SegmentUpdater, 1, advanced.SegmentWorkers, advanced.SegmentQueueSize, logger, appMonitorMock), - SplitSyncTask: tasks.NewFetchSplitsTask(workers.SplitUpdater, 1, logger), TelemetrySyncTask: tasks.NewRecordTelemetryTask(workers.TelemetryRecorder, 1, logger), } - workers.TelemetryRecorder.SynchronizeStats() syncForTest := NewSynchronizer(advanced, splitTasks, workers, logger, nil) syncForTest.StartPeriodicDataRecording() - time.Sleep(time.Second * 2) - if atomic.LoadInt64(&impressionsCalled) < 1 { - t.Error("It should be called once") - } - if atomic.LoadInt64(&eventsCalled) < 1 { - t.Error("It should be called once") - } - if atomic.LoadInt64(&statsCalled) < 1 { - t.Error("It should be called once") - } + time.Sleep(time.Millisecond * 1200) + assert.Equal(t, int64(1), atomic.LoadInt64(&impressionsCalled)) + assert.Equal(t, int64(1), atomic.LoadInt64(&eventsCalled)) + assert.Equal(t, int64(1), atomic.LoadInt64(&statsCalled)) syncForTest.StopPeriodicDataRecording() - time.Sleep(time.Second * 1) - if atomic.LoadInt64(&impressionsCalled) < 2 { - t.Error("It should be called two times") - } - if atomic.LoadInt64(&eventsCalled) < 2 { - t.Error("It should be called two times") - } - if atomic.LoadInt64(&statsCalled) < 2 { - t.Error("It should be called two times") + assert.Equal(t, int64(2), atomic.LoadInt64(&impressionsCalled)) + assert.Equal(t, int64(2), atomic.LoadInt64(&eventsCalled)) + assert.Equal(t, int64(2), atomic.LoadInt64(&statsCalled)) + splitMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) +} + +func TestSplitUpdateWorkerCNGreaterThanFFChange(t *testing.T) { + logger := logging.NewLogger(&logging.LoggerOptions{}) + splitFetcher := &httpMocks.MockSplitFetcher{} + splitAPI := api.SplitAPI{SplitFetcher: splitFetcher} + splitMockStorage := &storageMock.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(2), nil).Once() + segmentMockStorage := storageMock.MockSegmentStorage{} + telemetryMockStorage := storageMock.MockTelemetryStorage{} + appMonitorMock := &hcMock.ApplicationMonitorMock{} + ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} + largeSegmentStorage := &mocks.MockLargeSegmentStorage{} + + ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, goClientFeatureFlagsRules, goClientRuleBasedSegmentRules, logger, nil) + workers := Workers{ + SplitUpdater: split.NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitAPI.SplitFetcher, logger, telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder), + SegmentUpdater: segment.NewSegmentUpdater(splitMockStorage, segmentMockStorage, ruleBasedSegmentMockStorage, splitAPI.SegmentFetcher, logger, telemetryMockStorage, appMonitorMock), } - if atomic.LoadInt64(¬ifyEventCalled) != 0 { - t.Error("It should not be called") + splitTasks := SplitTasks{ + SegmentSyncTask: tasks.NewFetchSegmentsTask(workers.SegmentUpdater, 1, 1, 500, logger, appMonitorMock), + SplitSyncTask: tasks.NewFetchSplitsTask(workers.SplitUpdater, 1, logger), } + syncForTest := NewSynchronizer(conf.AdvancedConfig{}, splitTasks, workers, logger, nil) + + splitQueue := make(chan dtos.SplitChangeUpdate, 5000) + splitWorker, _ := push.NewSplitUpdateWorker(splitQueue, syncForTest, logger) + splitWorker.Start() + + // Testing Storage With Changenumber Greater Than FF + splitQueue <- *dtos.NewSplitChangeUpdate( + dtos.NewBaseUpdate(dtos.NewBaseMessage(0, "some"), 1), nil, nil, + ) + + time.Sleep(100 * time.Millisecond) + assert.True(t, splitWorker.IsRunning(), "It should be running") + + splitFetcher.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) } +/* func TestSplitUpdateWorkerCNGreaterThanFFChange(t *testing.T) { var splitFetchCalled int64 logger := logging.NewLogger(&logging.LoggerOptions{}) @@ -969,26 +893,14 @@ func TestSplitUpdateWorkerFFPcnDifferentStorageCN(t *testing.T) { t.Error("should have been called once. got: ", u) } } +*/ func TestLocalKill(t *testing.T) { logger := logging.NewLogger(&logging.LoggerOptions{}) splitAPI := api.SplitAPI{} - splitMockStorage := storageMock.MockSplitStorage{ - KillLocallyCall: func(splitName, defaultTreatment string, changeNumber int64) { - if splitName != "split" { - t.Error("Wrong splitName") - } - if defaultTreatment != "default_treatment" { - t.Error("Wrong defaultTreatment") - } - if changeNumber != 123456789 { - t.Error("Wrong changeNumber") - } - }, - } + splitMockStorage := &storageMock.SplitStorageMock{} + splitMockStorage.On("KillLocally", "split", "default_treatment", int64(123456789)).Return(nil) ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Maybe().Return(-1) - largeSegmentStorage := &mocks.MockLargeSegmentStorage{} ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, goClientFeatureFlagsRules, goClientRuleBasedSegmentRules, logger, nil) @@ -1000,44 +912,34 @@ func TestLocalKill(t *testing.T) { } syncForTest := NewSynchronizer(conf.AdvancedConfig{}, splitTasks, workers, logger, nil) syncForTest.LocalKill("split", "default_treatment", 123456789) + + splitMockStorage.AssertExpectations(t) } + func TestSplitUpdateWithReferencedSegments(t *testing.T) { - var ffUpdateCalled int64 + // Should we move to Split Worker var segmentUpdateCalled int64 var segmentFetchCalled int64 var recordUpdateCall int64 logger := logging.NewLogger(&logging.LoggerOptions{}) - splitAPI := api.SplitAPI{SegmentFetcher: httpMocks.MockSegmentFetcher{ - FetchCall: func(name string, fetchOptions *service.SegmentRequestParams) (*dtos.SegmentChangesDTO, error) { - atomic.AddInt64(&segmentFetchCalled, 1) - if name != "segment1" { - t.Error("Wrong name") - } - validReqParams(t, fetchOptions) - return &dtos.SegmentChangesDTO{ - Name: name, - Added: []string{"some"}, - Removed: []string{}, - Since: 123, - Till: 123, - }, nil - }, - }} - splitMockStorage := storageMock.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { - return 1, nil - }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { - if len(toAdd) != 1 { - t.Error("toAdd should have one feature flag") - } - if len(toRemove) != 0 { - t.Error("toRemove should be empty") - } - atomic.AddInt64(&ffUpdateCalled, 1) - }, - SegmentNamesCall: func() *set.ThreadUnsafeSet { return set.NewSet("segment1") }, - } + splitAPI := api.SplitAPI{ + SegmentFetcher: httpMocks.MockSegmentFetcher{ + FetchCall: func(name string, fetchOptions *service.SegmentRequestParams) (*dtos.SegmentChangesDTO, error) { + atomic.AddInt64(&segmentFetchCalled, 1) + assert.Equal(t, "segment1", name) + validReqParams(t, fetchOptions) + return &dtos.SegmentChangesDTO{ + Name: name, + Added: []string{"some"}, + Removed: []string{}, + Since: 123, + Till: 123, + }, nil + }, + }} + splitMockStorage := &storageMock.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(1), nil).Once() + splitMockStorage.On("Update", mock.Anything, mock.Anything, int64(2)).Return().Once() segmentMockStorage := storageMock.MockSegmentStorage{ ChangeNumberCall: func(segmentName string) (int64, error) { if segmentName != "segment1" { @@ -1048,9 +950,7 @@ func TestSplitUpdateWithReferencedSegments(t *testing.T) { }, UpdateCall: func(name string, toAdd *set.ThreadUnsafeSet, toRemove *set.ThreadUnsafeSet, changeNumber int64) error { atomic.AddInt64(&segmentUpdateCalled, 1) - if name != "segment1" { - t.Error("Wrong name") - } + assert.Equal(t, "segment1", name) return nil }, } @@ -1061,12 +961,9 @@ func TestSplitUpdateWithReferencedSegments(t *testing.T) { RecordSuccessfulSyncCall: func(resource int, time time.Time) {}, RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, } - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) {}, - } - + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything) ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Maybe().Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} @@ -1095,41 +992,28 @@ func TestSplitUpdateWithReferencedSegments(t *testing.T) { ) time.Sleep(300 * time.Millisecond) - - if u := atomic.LoadInt64(&ffUpdateCalled); u != 1 { - t.Error("should haven been called. got: ", u) - } - if s := atomic.LoadInt64(&segmentFetchCalled); s != 1 { - t.Error("should haven been called. got: ", s) - } - if s := atomic.LoadInt64(&segmentUpdateCalled); s != 1 { - t.Error("should haven been called. got: ", s) - } - if r := atomic.LoadInt64(&recordUpdateCall); r != 1 { - t.Error("should haven been called. got: ", r) - } + assert.Equal(t, int64(1), atomic.LoadInt64(&segmentFetchCalled)) + assert.Equal(t, int64(1), atomic.LoadInt64(&segmentUpdateCalled)) + assert.Equal(t, int64(1), atomic.LoadInt64(&recordUpdateCall)) + splitMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) } -// Large Segment test cases func TestSyncAllWithLargeSegmentLazyLoad(t *testing.T) { - // Updaters - var segmentUpdater syncMocks.SegmentUpdaterMock + segmentUpdater := &syncMocks.SegmentUpdaterMock{} segmentUpdater.On("SynchronizeSegments").Return(map[string]segment.UpdateResult{}, nil).Once() - - var splitUpdater syncMocks.SplitUpdaterMock + splitUpdater := &syncMocks.SplitUpdaterMock{} splitUpdater.On("SynchronizeSplits", (*int64)(nil)).Return(&split.UpdateResult{}, nil).Once() - - var lsUpdater syncMocks.LargeSegmentUpdaterMock + lsUpdater := &syncMocks.LargeSegmentUpdaterMock{} lsUpdater.On("SynchronizeLargeSegments").Return(map[string]*int64{}, nil).Once() - // Workers workers := Workers{ - SegmentUpdater: &segmentUpdater, - SplitUpdater: &splitUpdater, - LargeSegmentUpdater: &lsUpdater, + SegmentUpdater: segmentUpdater, + SplitUpdater: splitUpdater, + LargeSegmentUpdater: lsUpdater, } - // Config cfn := conf.AdvancedConfig{ LargeSegment: &conf.LargeSegmentConfig{ Enable: true, @@ -1137,37 +1021,27 @@ func TestSyncAllWithLargeSegmentLazyLoad(t *testing.T) { }, } - // Sync sync := NewSynchronizer(cfn, SplitTasks{}, workers, logging.NewLogger(&logging.LoggerOptions{}), nil) sync.SyncAll() - - time.Sleep(time.Millisecond * 1000) - + time.Sleep(100 * time.Millisecond) segmentUpdater.AssertExpectations(t) splitUpdater.AssertExpectations(t) lsUpdater.AssertExpectations(t) } func TestSyncAllWithLargeSegmentLazyLoadFalse(t *testing.T) { - var segmentUpdater syncMocks.SegmentUpdaterMock + segmentUpdater := &syncMocks.SegmentUpdaterMock{} segmentUpdater.On("SynchronizeSegments").Return(map[string]segment.UpdateResult{}, nil).Once() - - var splitUpdater syncMocks.SplitUpdaterMock + splitUpdater := &syncMocks.SplitUpdaterMock{} splitUpdater.On("SynchronizeSplits", (*int64)(nil)).Return(&split.UpdateResult{}, nil).Once() - - var lsUpdater syncMocks.LargeSegmentUpdaterMock + lsUpdater := &syncMocks.LargeSegmentUpdaterMock{} lsUpdater.On("SynchronizeLargeSegments").Return(map[string]*int64{}, nil).Once() - - // Workers workers := Workers{ - SegmentUpdater: &segmentUpdater, - SplitUpdater: &splitUpdater, - LargeSegmentUpdater: &lsUpdater, + SegmentUpdater: segmentUpdater, + SplitUpdater: splitUpdater, + LargeSegmentUpdater: lsUpdater, } - - // Tasks splitTasks := SplitTasks{} - cfn := conf.AdvancedConfig{ LargeSegment: &conf.LargeSegmentConfig{ Enable: true, @@ -1185,109 +1059,68 @@ func TestSyncAllWithLargeSegmentLazyLoadFalse(t *testing.T) { func TestSynchronizeLargeSegment(t *testing.T) { lsName := "ls_test" - // Updaters var cn *int64 var lsUpdater syncMocks.LargeSegmentUpdaterMock lsUpdater.On("SynchronizeLargeSegment", lsName, (*int64)(nil)).Return(cn, nil).Once() - // Workers workers := Workers{LargeSegmentUpdater: &lsUpdater} - sync := NewSynchronizer(conf.AdvancedConfig{}, SplitTasks{}, workers, logging.NewLogger(&logging.LoggerOptions{}), nil) err := sync.SynchronizeLargeSegment(lsName, nil) - if err != nil { - t.Error("Error should be nil") - } + assert.Nil(t, err) lsUpdater.AssertExpectations(t) } func TestSynchronizeLargeSegmentWithoutUpdaters(t *testing.T) { lsName := "ls_test" - - // Updaters var lsUpdater syncMocks.LargeSegmentUpdaterMock - - // Workers workers := Workers{} - sync := NewSynchronizer(conf.AdvancedConfig{}, SplitTasks{}, workers, logging.NewLogger(&logging.LoggerOptions{}), nil) - err := sync.SynchronizeLargeSegment(lsName, nil) - if err != nil { - t.Error("Error should be nil") - } - + assert.Nil(t, err) lsUpdater.AssertExpectations(t) } func TestSynchronizeLargeSegmentsWithoutUpdaters(t *testing.T) { - // Updaters var lsUpdater syncMocks.LargeSegmentUpdaterMock - - // Workers workers := Workers{} - sync := NewSynchronizer(conf.AdvancedConfig{}, SplitTasks{}, workers, logging.NewLogger(&logging.LoggerOptions{}), nil) - err := sync.(*SynchronizerImpl).synchronizeLargeSegments() - if err != nil { - t.Error("Error should be nil") - } - + assert.Nil(t, err) lsUpdater.AssertExpectations(t) } func TestFilterCachedLargeSegmentsWithoutUpdater(t *testing.T) { lsNames := []string{"ls1", "ls2", "ls3"} - - // Updaters var lsUpdater syncMocks.LargeSegmentUpdaterMock - - // Workers workers := Workers{} - sync := NewSynchronizer(conf.AdvancedConfig{}, SplitTasks{}, workers, logging.NewLogger(&logging.LoggerOptions{}), nil) - filtered := sync.(*SynchronizerImpl).filterCachedLargeSegments(lsNames) - if len(filtered) != 0 { - t.Error("filtered len should be 0. Actual: ", len(filtered)) - } - + assert.Empty(t, filtered) lsUpdater.AssertExpectations(t) } func TestFilterCachedLargeSegments(t *testing.T) { lsNames := []string{"ls1", "ls2", "ls3"} - - // Updaters var lsUpdater syncMocks.LargeSegmentUpdaterMock lsUpdater.On("IsCached", "ls1").Return(true).Once() lsUpdater.On("IsCached", "ls2").Return(false).Once() lsUpdater.On("IsCached", "ls3").Return(true).Once() - // Workers workers := Workers{ LargeSegmentUpdater: &lsUpdater, } - sync := NewSynchronizer(conf.AdvancedConfig{}, SplitTasks{}, workers, logging.NewLogger(&logging.LoggerOptions{}), nil) filtered := sync.(*SynchronizerImpl).filterCachedLargeSegments(lsNames) - if len(filtered) != 1 { - t.Error("filtered len should be 1. Actual: ", len(filtered)) - } - if filtered[0] != "ls2" { - t.Error("Filtered name should be ls2. Actual: ", filtered[0]) - } + assert.Len(t, filtered, 1) + assert.Equal(t, "ls2", filtered[0]) lsUpdater.AssertExpectations(t) } func TestSynchronizeLargeSegmentsAfterSplitSync(t *testing.T) { lsNames := []string{"ls1", "ls2", "ls3"} - - // Updaters var cn *int64 var lsUpdater syncMocks.LargeSegmentUpdaterMock lsUpdater.On("SynchronizeLargeSegment", "ls1", (*int64)(nil)).Return(cn, nil).Once() @@ -1296,17 +1129,12 @@ func TestSynchronizeLargeSegmentsAfterSplitSync(t *testing.T) { lsUpdater.On("IsCached", "ls1").Return(false).Once() lsUpdater.On("IsCached", "ls2").Return(false).Once() lsUpdater.On("IsCached", "ls3").Return(false).Once() - - // Workers workers := Workers{ LargeSegmentUpdater: &lsUpdater, } - sync := NewSynchronizer(conf.AdvancedConfig{}, SplitTasks{}, workers, logging.NewLogger(&logging.LoggerOptions{}), nil) sync.(*SynchronizerImpl).synchronizeLargeSegmentsAfterSplitSync(lsNames) - time.Sleep(time.Millisecond * 1000) - lsUpdater.AssertExpectations(t) } @@ -1341,14 +1169,10 @@ func TestStartAndStopFetchingWithLargeSegmentTask(t *testing.T) { SplitUpdater: &splitUpdater, SegmentUpdater: &segmentUpdater, } - splitMockStorage := storageMock.MockSplitStorage{ - LargeSegmentNamesCall: func() *set.ThreadUnsafeSet { - return set.NewSet("ls1", "ls2", "ls3") - }, - } - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) {}, - } + splitMockStorage := &storageMock.SplitStorageMock{} + splitMockStorage.On("LargeSegmentNames").Return(set.NewSet("ls1", "ls2", "ls3")).Once() + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything) splitTasks := SplitTasks{ SegmentSyncTask: tasks.NewFetchSegmentsTask(workers.SegmentUpdater, 1, advanced.SegmentWorkers, advanced.SegmentQueueSize, logger, appMonitorMock), SplitSyncTask: tasks.NewFetchSplitsTask(workers.SplitUpdater, 1, logger), @@ -1362,6 +1186,8 @@ func TestStartAndStopFetchingWithLargeSegmentTask(t *testing.T) { segmentUpdater.AssertExpectations(t) splitUpdater.AssertExpectations(t) lsUpdater.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) sync.StopPeriodicFetching() } @@ -1409,9 +1235,7 @@ func TestSynchronizeLargeSegmentUpdate(t *testing.T) { sync := NewSynchronizer(conf.AdvancedConfig{}, SplitTasks{}, workers, logging.NewLogger(&logging.LoggerOptions{}), nil) err := sync.SynchronizeLargeSegmentUpdate(dto) - if err != nil { - t.Error("Error should be nil. Actual:", err) - } + assert.Nil(t, err) lsUpdater.AssertExpectations(t) } @@ -1430,9 +1254,7 @@ func TestSynchronizeLargeSegmentUpdateNotCached(t *testing.T) { sync := NewSynchronizer(conf.AdvancedConfig{}, SplitTasks{}, workers, logging.NewLogger(&logging.LoggerOptions{}), nil) err := sync.SynchronizeLargeSegmentUpdate(dto) - if err != nil { - t.Error("Error should be nil. Actual:", err) - } + assert.Nil(t, err) lsUpdater.AssertExpectations(t) } diff --git a/synchronizer/worker/split/split.go b/synchronizer/worker/split/split.go index e6f6c560..6c149b09 100644 --- a/synchronizer/worker/split/split.go +++ b/synchronizer/worker/split/split.go @@ -385,7 +385,7 @@ func (s *UpdaterImpl) processRuleBasedChangeUpdate(ruleBasedChange dtos.SplitCha return &UpdateResult{ ReferencedSegments: segments, - NewChangeNumber: ruleBasedChange.BaseUpdate.ChangeNumber(), + NewRBChangeNumber: ruleBasedChange.BaseUpdate.ChangeNumber(), RequiresFetch: false, } } diff --git a/synchronizer/worker/split/split_test.go b/synchronizer/worker/split/split_test.go index b9fcb328..85330a58 100644 --- a/synchronizer/worker/split/split_test.go +++ b/synchronizer/worker/split/split_test.go @@ -2,8 +2,6 @@ package split import ( "errors" - "net/http" - "sync/atomic" "testing" "time" @@ -17,8 +15,11 @@ import ( "github.com/splitio/go-split-commons/v7/storage/inmemory/mutexmap" "github.com/splitio/go-split-commons/v7/storage/mocks" "github.com/splitio/go-split-commons/v7/telemetry" + "github.com/splitio/go-toolkit/v5/common" "github.com/splitio/go-toolkit/v5/logging" + + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -31,114 +32,68 @@ var syncProxyRuleBasedSegmentRules = []string{grammar.MatcherTypeAllKeys, gramma grammar.MatcherTypeEqualToBoolean, grammar.MatcherTypeMatchesString, grammar.MatcherEqualToSemver, grammar.MatcherTypeGreaterThanOrEqualToSemver, grammar.MatcherTypeLessThanOrEqualToSemver, grammar.MatcherTypeBetweenSemver, grammar.MatcherTypeInListSemver, grammar.MatcherTypeInLargeSegment, grammar.MatcherTypeInRuleBasedSegment} -func validReqParams(t *testing.T, fetchOptions service.RequestParams, till string) { - req, _ := http.NewRequest("GET", "test", nil) - fetchOptions.Apply(req) - if req.Header.Get("Cache-Control") != "no-cache" { - t.Error("Wrong header") - } - if req.URL.Query().Get("till") != till { - t.Error("Wrong till") - } -} - func TestSplitSynchronizerError(t *testing.T) { - var notifyEventCalled int64 - - splitMockStorage := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return -1, nil }, - } + splitMockStorage := &mocks.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(-1), nil) - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - if fetchOptions.ChangeNumber() != -1 { - t.Error("Wrong changenumber passed") - } - return nil, &dtos.HTTPError{Code: 500, Message: "some"} - }, - } + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", mock.MatchedBy(func(req *service.FlagRequestParams) bool { + return req.ChangeNumber() == -1 + })).Return(nil, &dtos.HTTPError{Code: 500, Message: "some"}).Once() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(int64(-1)) telemetryMockStorage := mocks.MockTelemetryStorage{ RecordSyncErrorCall: func(resource, status int) { - if resource != telemetry.SplitSync { - t.Error("It should be splits") - } - if status != 500 { - t.Error("Status should be 500") - } + assert.Equal(t, telemetry.SplitSync, resource) + assert.Equal(t, 500, status) }, } - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Once() ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, nil, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) splitUpdater := NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) _, err := splitUpdater.SynchronizeSplits(nil) - if err == nil { - t.Error("It should return err") - } - if atomic.LoadInt64(¬ifyEventCalled) != 1 { - t.Error("It should be called once") - } + assert.NotNil(t, err) + + splitMockStorage.AssertExpectations(t) + splitMockFetcher.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) } func TestSplitSynchronizerErrorScRequestURITooLong(t *testing.T) { - var notifyEventCalled int64 - var fetchCall int64 + splitMockStorage := &mocks.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(-1), nil) - splitMockStorage := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return -1, nil }, - } - - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&fetchCall, 1) - if fetchOptions.ChangeNumber() != -1 { - t.Error("Wrong changenumber passed") - } - return nil, &dtos.HTTPError{Code: 414, Message: "some"} - }, - } + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", mock.MatchedBy(func(req *service.FlagRequestParams) bool { + return req.ChangeNumber() == -1 + })).Return(nil, &dtos.HTTPError{Code: 414, Message: "some"}).Once() telemetryMockStorage := mocks.MockTelemetryStorage{ RecordSyncErrorCall: func(resource, status int) { - if resource != telemetry.SplitSync { - t.Error("It should be splits") - } - if status != 414 { - t.Error("Status should be 414") - } + assert.Equal(t, telemetry.SplitSync, resource) + assert.Equal(t, 414, status) }, } ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(int64(-1)) + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Once() ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, nil, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) splitUpdater := NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) _, err := splitUpdater.SynchronizeSplits(nil) - if err == nil { - t.Error("It should return err") - } - if atomic.LoadInt64(¬ifyEventCalled) != 1 { - t.Error("It should be called once") - } - if atomic.LoadInt64(&fetchCall) != 1 { - t.Error("fetchCall should be called once") - } + assert.NotNil(t, err) + splitMockStorage.AssertExpectations(t) + splitMockFetcher.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) } func TestSplitSynchronizer(t *testing.T) { @@ -146,73 +101,33 @@ func TestSplitSynchronizer(t *testing.T) { mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} mockedSplit2 := dtos.SplitDTO{Name: "split2", Killed: true, Status: "ACTIVE", TrafficTypeName: "two"} mockedSplit3 := dtos.SplitDTO{Name: "split3", Killed: true, Status: "INACTIVE", TrafficTypeName: "one"} - var notifyEventCalled int64 - splitMockStorage := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { - return -1, nil - }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { - if changeNumber != 3 { - t.Error("Wrong changenumber") - } - if len(toAdd) != 2 { - t.Error("Wrong length of passed splits") - } - s1 := toAdd[0] - if s1.Name != "split1" || s1.Killed { - t.Error("split1 stored/retrieved incorrectly") - t.Error(s1) - } - s2 := toAdd[1] - if s2.Name != "split2" || !s2.Killed { - t.Error("split2 stored/retrieved incorrectly") - t.Error(s2) - } - }, - RemoveCall: func(splitname string) { - if splitname != "split3" { - t.Error("It should remove split3") - } - }, - } + splitMockStorage := &mocks.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(-1), nil) + splitMockStorage.On("Update", []dtos.SplitDTO{mockedSplit1, mockedSplit2}, []dtos.SplitDTO{mockedSplit3}, int64(3)).Once() - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - if fetchOptions.ChangeNumber() != -1 { - t.Error("Wrong since") - } - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2, mockedSplit3}, - Since: 3, - Till: 3}, - }, nil - }, - } + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2, mockedSplit3}, + Since: 3, + Till: 3, + }}, nil).Once() telemetryMockStorage := mocks.MockTelemetryStorage{ RecordSuccessfulSyncCall: func(resource int, tm time.Time) { - if resource != telemetry.SplitSync { - t.Error("Resource should be splits") - } - if tm.Before(before) { - t.Error("It should be higher than before") - } + assert.Equal(t, telemetry.SplitSync, resource) + assert.True(t, tm.After(before)) }, RecordSyncLatencyCall: func(resource int, tm time.Duration) { - if resource != telemetry.SplitSync { - t.Error("Resource should be splits") - } + assert.Equal(t, telemetry.SplitSync, resource) }, } - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Once() + ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(int64(-1)) ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Once().Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} @@ -221,17 +136,15 @@ func TestSplitSynchronizer(t *testing.T) { splitUpdater := NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) _, err := splitUpdater.SynchronizeSplits(nil) - if err != nil { - t.Error("It should not return err") - } - if atomic.LoadInt64(¬ifyEventCalled) != 1 { - t.Error("It should be called once") - } + assert.Nil(t, err) + + splitMockStorage.AssertExpectations(t) + splitMockFetcher.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) } func TestSplitSyncProcess(t *testing.T) { - var call int64 - var notifyEventCalled int64 mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} mockedSplit2 := dtos.SplitDTO{Name: "split2", Killed: true, Status: "ACTIVE", TrafficTypeName: "two"} mockedSplit3 := dtos.SplitDTO{Name: "split3", Killed: true, Status: "INACTIVE", TrafficTypeName: "one"} @@ -243,48 +156,28 @@ func TestSplitSyncProcess(t *testing.T) { }}}}, } - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&call, 1) - switch call { - case 1: - if fetchOptions.ChangeNumber() != -1 { - t.Error("Wrong changenumber passed") - } - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2, mockedSplit3}, - Since: 3, - Till: 3}, - }, nil - case 2: - if fetchOptions.ChangeNumber() != 3 { - t.Error("Wrong changenumber passed") - } - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit4, mockedSplit5}, - Since: 3, - Till: 3}, - }, nil - default: - t.Error("Wrong calls") - return nil, errors.New("some") - } - }, - } + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2, mockedSplit3}, + Since: 3, + Till: 3}, + }, nil).Once() + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit4, mockedSplit5}, + Since: 3, + Till: 3}, + }, nil).Once() - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Times(2) splitStorage := mutexmap.NewMMSplitStorage(flagsets.NewFlagSetFilter(nil)) splitStorage.Update([]dtos.SplitDTO{{}}, nil, -1) telemetryStorage, _ := inmemory.NewTelemetryStorage() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Return(-1) - ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Times(3).Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(-1)) + ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Times(2).Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} @@ -292,99 +185,52 @@ func TestSplitSyncProcess(t *testing.T) { splitUpdater := NewSplitUpdater(splitStorage, ruleBasedSegmentMockStorage, splitMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) res, err := splitUpdater.SynchronizeSplits(nil) - if err != nil { - t.Error("It should not return err") - } - - if len(res.ReferencedSegments) != 0 { - t.Error("invalid referenced segment names. Got: ", res.ReferencedSegments) - } - - if !splitStorage.TrafficTypeExists("one") { - t.Error("It should exists") - } - - if !splitStorage.TrafficTypeExists("two") { - t.Error("It should exists") - } + assert.Nil(t, err) + assert.Len(t, res.ReferencedSegments, 0) + assert.True(t, splitStorage.TrafficTypeExists("one")) + assert.True(t, splitStorage.TrafficTypeExists("two")) res, err = splitUpdater.SynchronizeSplits(nil) - if err != nil { - t.Error("It should not return err") - } - - if len(res.ReferencedSegments) != 1 || res.ReferencedSegments[0] != "someSegment" { - t.Error("invalid referenced segment names. Got: ", res.ReferencedSegments) - } - - s1 := splitStorage.Split("split1") - if s1 != nil { - t.Error("split1 should have been removed") - } - - s2 := splitStorage.Split("split2") - if s2 == nil || s2.Name != "split2" || !s2.Killed { - t.Error("split2 stored/retrieved incorrectly") - t.Error(s2) - } - - s3 := splitStorage.Split("split3") - if s3 != nil { - t.Error("split3 should have been removed") - } - - s4 := splitStorage.Split("split4") - if s4 == nil || s4.Name != "split4" || s4.Killed { - t.Error("split4 stored/retrieved incorrectly") - t.Error(s4) - } - - if splitStorage.TrafficTypeExists("one") { - t.Error("It should not exists") - } - - if !splitStorage.TrafficTypeExists("two") { - t.Error("It should exists") - } - - if atomic.LoadInt64(¬ifyEventCalled) != 2 { - t.Error("It should be called twice") - } + assert.Nil(t, err) + assert.Len(t, res.ReferencedSegments, 1) + assert.Equal(t, res.ReferencedSegments[0], "someSegment") + + assert.Nil(t, splitStorage.Split("split1")) + assert.Equal(t, mockedSplit2, *splitStorage.Split("split2")) + assert.Nil(t, splitStorage.Split("split3")) + assert.Equal(t, mockedSplit5, *splitStorage.Split("split4")) + assert.False(t, splitStorage.TrafficTypeExists("one")) + assert.True(t, splitStorage.TrafficTypeExists("two")) + + appMonitorMock.AssertExpectations(t) + splitMockFetcher.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) } func TestSplitTill(t *testing.T) { - var call int64 - var notifyEventCalled int64 mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} mockedRuleBased1 := dtos.RuleBasedSegmentDTO{Name: "rb1", Status: "ACTIVE"} - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&call, 1) - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, - Since: 2, - Till: 2}, - RuleBasedSegments: dtos.RuleBasedSegmentsDTO{RuleBasedSegments: []dtos.RuleBasedSegmentDTO{mockedRuleBased1}, - Since: 2, - Till: 2}, - }, nil - }, - } + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, + Since: 2, + Till: 2}, + RuleBasedSegments: dtos.RuleBasedSegmentsDTO{RuleBasedSegments: []dtos.RuleBasedSegmentDTO{mockedRuleBased1}, + Since: 2, + Till: 2}, + }, nil).Twice() - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Times(2) splitStorage := mutexmap.NewMMSplitStorage(flagsets.NewFlagSetFilter(nil)) splitStorage.Update([]dtos.SplitDTO{{}}, nil, -1) telemetryStorage, _ := inmemory.NewTelemetryStorage() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Times(12).Return(-1) - ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Times(12).Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Times(4).Return(int64(-1)) + ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Times(2).Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) @@ -392,73 +238,45 @@ func TestSplitTill(t *testing.T) { var till int64 = 1 _, err := splitUpdater.SynchronizeSplits(&till) - if err != nil { - t.Error("It should not return err") - } + assert.Nil(t, err) _, err = splitUpdater.SynchronizeSplits(&till) - if err != nil { - t.Error("It should not return err") - } - if atomic.LoadInt64(&call) != 2 { - t.Error("It should be called once") - } - if atomic.LoadInt64(¬ifyEventCalled) != 2 { - t.Error("It should be called twice") - } + assert.Nil(t, err) + + splitMockFetcher.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) } func TestByPassingCDN(t *testing.T) { - var call int64 - var notifyEventCalled int64 mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} - - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&call, 1) - switch called := atomic.LoadInt64(&call); { - case called == 1: - validReqParams(t, fetchOptions, "") - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, - Since: 1, - Till: 2}, - }, nil - case called >= 2 && called <= 11: - validReqParams(t, fetchOptions, "") - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, - Since: 2, - Till: 2}, - }, nil - case called == 12: - validReqParams(t, fetchOptions, "2") - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, - Since: 3, - Till: 3}, - }, nil - } - - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, - Since: 2, - Till: 2}, - }, nil - }, - } - - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", service.MakeFlagRequestParams().WithChangeNumber(-1).WithChangeNumberRB(-1)).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, + Since: 1, + Till: 2}, + }, nil).Once() + splitMockFetcher.On("Fetch", service.MakeFlagRequestParams().WithChangeNumber(2).WithChangeNumberRB(-1)).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, + Since: 2, + Till: 2}, + }, nil).Times(10) + splitMockFetcher.On("Fetch", mock.MatchedBy(func(params *service.FlagRequestParams) bool { + return *params.Till() == 2 + })).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, + Since: 3, + Till: 3}, + }, nil).Once() + + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Once() splitStorage := mutexmap.NewMMSplitStorage(flagsets.NewFlagSetFilter(nil)) splitStorage.Update([]dtos.SplitDTO{{}}, nil, -1) telemetryStorage, _ := inmemory.NewTelemetryStorage() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Times(13).Return(-1) - ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Times(13).Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Times(13).Return(int64(-1)) + ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Times(12).Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) @@ -468,70 +286,44 @@ func TestByPassingCDN(t *testing.T) { var till int64 = 3 _, err := splitUpdater.SynchronizeSplits(&till) - if err != nil { - t.Error("It should not return err") - } - if atomic.LoadInt64(&call) != 12 { - t.Error("It should be called twelve times instead of", atomic.LoadInt64(&call)) - } - if atomic.LoadInt64(¬ifyEventCalled) != 1 { - t.Error("It should be called twice instead of", atomic.LoadInt64(¬ifyEventCalled)) - } + assert.Nil(t, err) + splitMockFetcher.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) } func TestByPassingCDNLimit(t *testing.T) { - var call int64 - var notifyEventCalled int64 mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&call, 1) - switch called := atomic.LoadInt64(&call); { - case called == 1: - validReqParams(t, fetchOptions, "") - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, - Since: 1, - Till: 2}, - }, nil - case called >= 2 && called <= 11: - validReqParams(t, fetchOptions, "") - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, - Since: 2, - Till: 2}, - }, nil - case called >= 12: - validReqParams(t, fetchOptions, "2") - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, - Since: 2, - Till: 2}, - }, nil - } - - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, - Since: 2, - Till: 2}, - }, nil - }, - } - - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", service.MakeFlagRequestParams().WithChangeNumber(-1).WithChangeNumberRB(-1)).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, + Since: 1, + Till: 2}, + }, nil).Once() + splitMockFetcher.On("Fetch", service.MakeFlagRequestParams().WithChangeNumber(2).WithChangeNumberRB(-1)).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, + Since: 2, + Till: 2}, + }, nil).Times(10) + splitMockFetcher.On("Fetch", mock.MatchedBy(func(params *service.FlagRequestParams) bool { + return *params.Till() == 2 + })).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1}, + Since: 2, + Till: 2}, + }, nil).Times(10) + + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Once() splitStorage := mutexmap.NewMMSplitStorage(flagsets.NewFlagSetFilter(nil)) splitStorage.Update([]dtos.SplitDTO{{}}, nil, -1) telemetryStorage, _ := inmemory.NewTelemetryStorage() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Times(22).Return(-1) - ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Times(22).Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Times(22).Return(int64(-1)) + ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Times(21).Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) @@ -541,38 +333,21 @@ func TestByPassingCDNLimit(t *testing.T) { var till int64 = 3 _, err := splitUpdater.SynchronizeSplits(&till) - if err != nil { - t.Error("It should not return err") - } - if atomic.LoadInt64(&call) != 21 { - t.Error("It should be called twenty one times instead of", atomic.LoadInt64(&call)) - } - if atomic.LoadInt64(¬ifyEventCalled) != 1 { - t.Error("It should be called twice instead of", atomic.LoadInt64(¬ifyEventCalled)) - } + assert.Nil(t, err) + + splitMockFetcher.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) } func TestProcessFFChange(t *testing.T) { - var fetchCallCalled int64 logger := logging.NewLogger(&logging.LoggerOptions{}) - ffStorageMock := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { - return 43, nil - }, - } - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&fetchCallCalled, 1) - return nil, nil - }, - } - + ffStorageMock := &mocks.SplitStorageMock{} + ffStorageMock.On("ChangeNumber").Return(int64(43), nil) + splitMockFetcher := &fetcherMock.MockSplitFetcher{} ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) - telemetryStorage, _ := inmemory.NewTelemetryStorage() - appMonitorMock := hcMock.MockApplicationMonitor{} - + appMonitorMock := &hcMock.ApplicationMonitorMock{} largeSegmentStorage := &mocks.MockLargeSegmentStorage{} ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) @@ -581,138 +356,73 @@ func TestProcessFFChange(t *testing.T) { result, _ := fetcher.SynchronizeFeatureFlags(dtos.NewSplitChangeUpdate( dtos.NewBaseUpdate(dtos.NewBaseMessage(0, "some"), 12), nil, nil, )) - if result.RequiresFetch { - t.Error("should be false") - } - if atomic.LoadInt64(&fetchCallCalled) != 0 { - t.Error("Fetch should not be called") - } + assert.False(t, result.RequiresFetch) + splitMockFetcher.AssertExpectations(t) + ffStorageMock.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) } func TestAddOrUpdateFeatureFlagNil(t *testing.T) { - var fetchCallCalled int64 logger := logging.NewLogger(&logging.LoggerOptions{}) - ffStorageMock := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { - return -1, nil - }, - UpdateCall: func(toAdd, toRemove []dtos.SplitDTO, changeNumber int64) {}, - } - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&fetchCallCalled, 1) - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Till: 2, - Since: 2, - Splits: []dtos.SplitDTO{}}, - }, nil - }, - } + ffStorageMock := &mocks.SplitStorageMock{} + ffStorageMock.On("ChangeNumber").Return(int64(-1), nil).Times(3) + ffStorageMock.On("Update", mock.Anything, mock.Anything, mock.Anything).Return(-1).Once() + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Till: 2, + Since: 2, + Splits: []dtos.SplitDTO{}}, + }, nil).Once() telemetryStorage, _ := inmemory.NewTelemetryStorage() - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) {}, - } - + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Once() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(int64(-1)) ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Once().Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} - ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) fetcher := NewSplitUpdater(ffStorageMock, ruleBasedSegmentMockStorage, splitMockFetcher, logger, telemetryStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) - fetcher.SynchronizeFeatureFlags(dtos.NewSplitChangeUpdate( dtos.NewBaseUpdate(dtos.NewBaseMessage(0, "some"), 2), nil, nil, )) - if atomic.LoadInt64(&fetchCallCalled) != 1 { - t.Error("Fetch should be called once") - } + + ffStorageMock.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) } func TestAddOrUpdateFeatureFlagPcnEquals(t *testing.T) { - var fetchCallCalled int64 - var updateCalled int64 logger := logging.NewLogger(&logging.LoggerOptions{}) - ffStorageMock := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { - return 2, nil - }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { - atomic.AddInt64(&updateCalled, 1) - if changeNumber != 4 { - t.Error("changenumber should be the one incomed from dto") - } - if len(toAdd) == 0 { - t.Error("toAdd should have a feature flag") - } - if len(toRemove) != 0 { - t.Error("toRemove shouldn't have a feature flag") - } - }, - } - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&fetchCallCalled, 1) - return nil, nil - }, - } - + ffStorageMock := &mocks.SplitStorageMock{} + ffStorageMock.On("ChangeNumber").Return(int64(2), nil) + ffStorageMock.On("Update", mock.Anything, mock.Anything, int64(4)) + splitMockFetcher := &fetcherMock.MockSplitFetcher{} ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) - telemetryStorage, _ := inmemory.NewTelemetryStorage() - appMonitorMock := hcMock.MockApplicationMonitor{} + appMonitorMock := &hcMock.ApplicationMonitorMock{} largeSegmentStorage := &mocks.MockLargeSegmentStorage{} - ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) fetcher := NewSplitUpdater(ffStorageMock, ruleBasedSegmentMockStorage, splitMockFetcher, logger, telemetryStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) - featureFlag := dtos.SplitDTO{ChangeNumber: 4, Status: Active} - fetcher.SynchronizeFeatureFlags(dtos.NewSplitChangeUpdate( dtos.NewBaseUpdate(dtos.NewBaseMessage(0, "some"), 4), common.Int64Ref(2), &featureFlag, )) - if atomic.LoadInt64(&fetchCallCalled) != 0 { - t.Error("It should not fetch splits") - } - if atomic.LoadInt64(&updateCalled) != 1 { - t.Error("It should update the storage") - } + ffStorageMock.AssertExpectations(t) + splitMockFetcher.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) } func TestAddOrUpdateFeatureFlagArchive(t *testing.T) { - var fetchCallCalled int64 - var updateCalled int64 logger := logging.NewLogger(&logging.LoggerOptions{}) - ffStorageMock := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { - return 2, nil - }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { - atomic.AddInt64(&updateCalled, 1) - if changeNumber != 4 { - t.Error("changenumber should be the one incomed from dto") - } - if len(toRemove) == 0 { - t.Error("toRemove should have a feature flag") - } - if len(toAdd) != 0 { - t.Error("toAdd shouldn't have a feature flag") - } - }, - } - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&fetchCallCalled, 1) - return nil, nil - }, - } - + ffStorageMock := &mocks.SplitStorageMock{} + ffStorageMock.On("ChangeNumber").Return(int64(2), nil).Once() + ffStorageMock.On("Update", mock.Anything, mock.Anything, int64(4)).Return().Once() + splitMockFetcher := &fetcherMock.MockSplitFetcher{} ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) - telemetryStorage, _ := inmemory.NewTelemetryStorage() - appMonitorMock := hcMock.MockApplicationMonitor{} + appMonitorMock := &hcMock.ApplicationMonitorMock{} largeSegmentStorage := &mocks.MockLargeSegmentStorage{} ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logger, nil) fetcher := NewSplitUpdater(ffStorageMock, ruleBasedSegmentMockStorage, splitMockFetcher, logger, telemetryStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) @@ -721,46 +431,30 @@ func TestAddOrUpdateFeatureFlagArchive(t *testing.T) { fetcher.SynchronizeFeatureFlags(dtos.NewSplitChangeUpdate( dtos.NewBaseUpdate(dtos.NewBaseMessage(0, "some"), 4), common.Int64Ref(2), &featureFlag, )) - if atomic.LoadInt64(&fetchCallCalled) != 0 { - t.Error("It should not fetch splits") - } - if atomic.LoadInt64(&updateCalled) != 1 { - t.Error("It should update the storage") - } + + splitMockFetcher.AssertExpectations(t) + ffStorageMock.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) } func TestAddOrUpdateFFCNFromStorageError(t *testing.T) { - var fetchCallCalled int64 - var updateCalled int64 logger := logging.NewLogger(&logging.LoggerOptions{}) - ffStorageMock := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { - return 0, errors.New("error geting change number") - }, - UpdateCall: func(toAdd, toRemove []dtos.SplitDTO, changeNumber int64) { - atomic.AddInt64(&updateCalled, 1) - if changeNumber != 2 { - t.Error("It should be 2") - } - }, - } - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&fetchCallCalled, 1) - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Till: 2, - Since: 2, - Splits: []dtos.SplitDTO{}}, - }, nil - }, - } + ffStorageMock := &mocks.SplitStorageMock{} + ffStorageMock.On("ChangeNumber").Return(int64(0), errors.New("error geting change number")).Times(3) + ffStorageMock.On("Update", mock.Anything, mock.Anything, int64(2)).Return().Once() + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Till: 2, + Since: 2, + Splits: []dtos.SplitDTO{}}, + }, nil) telemetryStorage, _ := inmemory.NewTelemetryStorage() - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) {}, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(int64(-1)) ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Once().Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} @@ -770,120 +464,66 @@ func TestAddOrUpdateFFCNFromStorageError(t *testing.T) { fetcher.SynchronizeFeatureFlags(dtos.NewSplitChangeUpdate( dtos.NewBaseUpdate(dtos.NewBaseMessage(0, "some"), 2), nil, nil, )) - if atomic.LoadInt64(&fetchCallCalled) != 1 { - t.Error("It should fetch splits") - } - if atomic.LoadInt64(&updateCalled) != 1 { - t.Error("It should update the storage") - } } func TestGetActiveFF(t *testing.T) { - var featureFlags []dtos.SplitDTO - featureFlags = append(featureFlags, dtos.SplitDTO{Status: Active}) - featureFlags = append(featureFlags, dtos.SplitDTO{Status: Active}) + featureFlags := []dtos.SplitDTO{{Status: Active}, {Status: Active}} featureFlagChanges := &dtos.SplitChangesDTO{FeatureFlags: dtos.FeatureFlagsDTO{Splits: featureFlags}} - ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} - ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) - s := NewSplitUpdater(mocks.MockSplitStorage{}, ruleBasedSegmentMockStorage, fetcherMock.MockSplitFetcher{}, nil, mocks.MockTelemetryStorage{}, hcMock.MockApplicationMonitor{}, flagsets.NewFlagSetFilter(nil), ruleBuilder) + s := NewSplitUpdater(mocks.MockSplitStorage{}, ruleBasedSegmentMockStorage, &fetcherMock.MockSplitFetcher{}, nil, mocks.MockTelemetryStorage{}, hcMock.MockApplicationMonitor{}, flagsets.NewFlagSetFilter(nil), ruleBuilder) actives, inactives := s.processFeatureFlagChanges(featureFlagChanges) - - if len(actives) != 2 { - t.Error("active length should be 2") - } - - if len(inactives) != 0 { - t.Error("incative length should be 0") - } + assert.Len(t, actives, 2) + assert.Len(t, inactives, 0) } func TestGetInactiveFF(t *testing.T) { - var featureFlags []dtos.SplitDTO - featureFlags = append(featureFlags, dtos.SplitDTO{Status: Archived}) - featureFlags = append(featureFlags, dtos.SplitDTO{Status: Archived}) + featureFlags := []dtos.SplitDTO{{Status: Archived}, {Status: Archived}} featureFlagChanges := &dtos.SplitChangesDTO{FeatureFlags: dtos.FeatureFlagsDTO{Splits: featureFlags}} - ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} - ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) - s := NewSplitUpdater(mocks.MockSplitStorage{}, ruleBasedSegmentMockStorage, fetcherMock.MockSplitFetcher{}, nil, mocks.MockTelemetryStorage{}, hcMock.MockApplicationMonitor{}, flagsets.NewFlagSetFilter(nil), ruleBuilder) + s := NewSplitUpdater(mocks.MockSplitStorage{}, ruleBasedSegmentMockStorage, &fetcherMock.MockSplitFetcher{}, nil, mocks.MockTelemetryStorage{}, hcMock.MockApplicationMonitor{}, flagsets.NewFlagSetFilter(nil), ruleBuilder) actives, inactives := s.processFeatureFlagChanges(featureFlagChanges) - - if len(actives) != 0 { - t.Error("active length should be 2") - } - - if len(inactives) != 2 { - t.Error("incative length should be 0") - } + assert.Len(t, actives, 0) + assert.Len(t, inactives, 2) } func TestGetActiveAndInactiveFF(t *testing.T) { - var featureFlags []dtos.SplitDTO - featureFlags = append(featureFlags, dtos.SplitDTO{Status: Active}) - featureFlags = append(featureFlags, dtos.SplitDTO{Status: Archived}) + featureFlags := []dtos.SplitDTO{{Status: Active}, {Status: Archived}} featureFlagChanges := &dtos.SplitChangesDTO{ FeatureFlags: dtos.FeatureFlagsDTO{Splits: featureFlags}} - ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) - s := NewSplitUpdater(mocks.MockSplitStorage{}, ruleBasedSegmentMockStorage, fetcherMock.MockSplitFetcher{}, nil, mocks.MockTelemetryStorage{}, hcMock.MockApplicationMonitor{}, flagsets.NewFlagSetFilter(nil), ruleBuilder) + s := NewSplitUpdater(mocks.MockSplitStorage{}, ruleBasedSegmentMockStorage, &fetcherMock.MockSplitFetcher{}, nil, mocks.MockTelemetryStorage{}, hcMock.MockApplicationMonitor{}, flagsets.NewFlagSetFilter(nil), ruleBuilder) actives, inactives := s.processFeatureFlagChanges(featureFlagChanges) - - if len(actives) != 1 { - t.Error("active length should be 2") - } - - if len(inactives) != 1 { - t.Error("incative length should be 0") - } + assert.Len(t, actives, 1) + assert.Len(t, inactives, 1) } func TestSplitSyncWithSets(t *testing.T) { - var call int64 mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one", Sets: []string{"set1", "set2"}} mockedSplit2 := dtos.SplitDTO{Name: "split2", Killed: false, Status: "ACTIVE", TrafficTypeName: "one", Sets: []string{"set4"}} mockedSplit3 := dtos.SplitDTO{Name: "split3", Killed: false, Status: "ACTIVE", TrafficTypeName: "one", Sets: []string{"set5", "set1"}} - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&call, 1) - switch call { - case 1: - if fetchOptions.ChangeNumber() != -1 { - t.Error("Wrong changenumber passed") - } - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2, mockedSplit3}, - Since: 3, - Till: 3}, - }, nil - default: - t.Error("Wrong calls") - return nil, errors.New("some") - } - }, - } - - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) {}, - } + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2, mockedSplit3}, + Since: 3, + Till: 3}, + }, nil).Once() + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Once() splitStorage := mutexmap.NewMMSplitStorage(flagsets.NewFlagSetFilter(nil)) splitStorage.Update([]dtos.SplitDTO{}, nil, -1) telemetryStorage, _ := inmemory.NewTelemetryStorage() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(int64(-1)) ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Once().Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} @@ -891,63 +531,38 @@ func TestSplitSyncWithSets(t *testing.T) { splitUpdater := NewSplitUpdater(splitStorage, ruleBasedSegmentMockStorage, splitMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryStorage, appMonitorMock, flagsets.NewFlagSetFilter([]string{"set1", "set2", "set3"}), ruleBuilder) res, err := splitUpdater.SynchronizeSplits(nil) - if err != nil { - t.Error("It should not return err") - } - - if len(res.ReferencedSegments) != 0 { - t.Error("invalid referenced segment names. Got: ", res.ReferencedSegments) - } - - if splitStorage.Split("split1") == nil { - t.Error("split1 should be present") - } - if splitStorage.Split("split2") != nil { - t.Error("split2 should not be present") - } - if splitStorage.Split("split3") == nil { - t.Error("split3 should be present") - } + assert.Nil(t, err) + assert.Len(t, res.ReferencedSegments, 0) + assert.NotNil(t, splitStorage.Split("split1")) + assert.Nil(t, splitStorage.Split("split2")) + assert.NotNil(t, splitStorage.Split("split3")) + splitMockFetcher.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) } func TestSplitSyncWithSetsInConfig(t *testing.T) { - var call int64 mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one", Sets: []string{"set1"}} mockedSplit2 := dtos.SplitDTO{Name: "split2", Killed: false, Status: "ACTIVE", TrafficTypeName: "one", Sets: []string{"set4"}} mockedSplit3 := dtos.SplitDTO{Name: "split3", Killed: false, Status: "ACTIVE", TrafficTypeName: "one", Sets: []string{"set5", "set2"}} mockedSplit4 := dtos.SplitDTO{Name: "split4", Killed: false, Status: "ACTIVE", TrafficTypeName: "one", Sets: []string{"set2"}} - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&call, 1) - switch call { - case 1: - if fetchOptions.ChangeNumber() != -1 { - t.Error("Wrong changenumber passed") - } - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{ - Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2, mockedSplit3, mockedSplit4}, - Since: 3, - Till: 3}, - }, nil - default: - t.Error("Wrong calls") - return nil, errors.New("some") - } - }, - } + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2, mockedSplit3, mockedSplit4}, + Since: 3, + Till: 3}, + }, nil).Once() - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) {}, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Once() flagSetFilter := flagsets.NewFlagSetFilter([]string{"set2", "set4"}) splitStorage := mutexmap.NewMMSplitStorage(flagSetFilter) splitStorage.Update([]dtos.SplitDTO{}, nil, -1) telemetryStorage, _ := inmemory.NewTelemetryStorage() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(int64(-1)) ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Once().Return(-1) largeSegmentStorage := &mocks.MockLargeSegmentStorage{} @@ -955,65 +570,31 @@ func TestSplitSyncWithSetsInConfig(t *testing.T) { splitUpdater := NewSplitUpdater(splitStorage, ruleBasedSegmentMockStorage, splitMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryStorage, appMonitorMock, flagSetFilter, ruleBuilder) res, err := splitUpdater.SynchronizeSplits(nil) - if err != nil { - t.Error("It should not return err") - } - - if len(res.ReferencedSegments) != 0 { - t.Error("invalid referenced segment names. Got: ", res.ReferencedSegments) - } - - s1 := splitStorage.Split("split1") - if s1 != nil { - t.Error("split1 should not be present") - } - s2 := splitStorage.Split("split2") - if s2 == nil { - t.Error("split2 should be present") - } - s3 := splitStorage.Split("split3") - if s3 == nil { - t.Error("split3 should be present") - } - s4 := splitStorage.Split("split4") - if s4 == nil { - t.Error("split4 should be present") - } + assert.Nil(t, err) + assert.Len(t, res.ReferencedSegments, 0) + assert.Nil(t, splitStorage.Split("split1")) + assert.NotNil(t, splitStorage.Split("split2")) + assert.NotNil(t, splitStorage.Split("split3")) + assert.NotNil(t, splitStorage.Split("split4")) + splitMockFetcher.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + splitMockFetcher.AssertExpectations(t) } func TestSynchronizeSplitsWithLowerTill(t *testing.T) { // Mock split storage with higher change number - currentSince := int64(100) - splitMockStorage := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return currentSince, nil }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) {}, - } + splitMockStorage := &mocks.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(100), nil) + splitMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Return() // Mock rule based segment storage with higher change number - currentRBSince := int64(150) ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int(currentRBSince)) + ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(150)) ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Return() // Mock fetcher - var fetchCalled bool - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(requestParams *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - fetchCalled = true - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{ - Since: currentSince, - Till: currentSince, - Splits: []dtos.SplitDTO{}, - }, - RuleBasedSegments: dtos.RuleBasedSegmentsDTO{ - Since: currentRBSince, - Till: currentRBSince, - RuleBasedSegments: []dtos.RuleBasedSegmentDTO{}, - }, - }, nil - }, - } + splitMockFetcher := &fetcherMock.MockSplitFetcher{} // Mock telemetry storage telemetryMockStorage := mocks.MockTelemetryStorage{ @@ -1022,9 +603,8 @@ func TestSynchronizeSplitsWithLowerTill(t *testing.T) { } // Mock app monitor - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) {}, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return() largeSegmentStorage := &mocks.MockLargeSegmentStorage{} ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) // Create split updater @@ -1042,223 +622,152 @@ func TestSynchronizeSplitsWithLowerTill(t *testing.T) { // Test case 1: till is less than both currentSince and currentRBSince till := int64(50) result, err := splitUpdater.SynchronizeSplits(&till) - - if err != nil { - t.Error("Expected no error, got:", err) - } - - if result == nil { - t.Error("Expected non-nil result") - } - - if fetchCalled { - t.Error("Fetcher should not have been called when till is less than both currentSince and currentRBSince") - } + assert.Nil(t, err) + assert.NotNil(t, result) + assert.Equal(t, &UpdateResult{}, result) // Test case 2: till is equal to currentSince but less than currentRBSince - till = currentSince + till = 100 + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{ + Since: 100, + Till: 100, + Splits: []dtos.SplitDTO{}, + }, + RuleBasedSegments: dtos.RuleBasedSegmentsDTO{ + Since: 150, + Till: 150, + RuleBasedSegments: []dtos.RuleBasedSegmentDTO{}, + }, + }, nil).Once() result, err = splitUpdater.SynchronizeSplits(&till) - - if err != nil { - t.Error("Expected no error when till equals currentSince, got:", err) - } - - if !fetchCalled { - t.Error("Fetcher should have been called when till equals currentSince (since currentRBSince is higher)") - } + assert.Nil(t, err) + assert.NotNil(t, result) + assert.Equal(t, int64(100), result.NewChangeNumber) + assert.Equal(t, int64(150), result.NewRBChangeNumber) // Test case 3: till is equal to currentRBSince but greater than currentSince - till = currentRBSince + till = 150 + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{ + Since: 100, + Till: 100, + Splits: []dtos.SplitDTO{}, + }, + RuleBasedSegments: dtos.RuleBasedSegmentsDTO{ + Since: 150, + Till: 150, + RuleBasedSegments: []dtos.RuleBasedSegmentDTO{}, + }, + }, nil).Once() result, err = splitUpdater.SynchronizeSplits(&till) - - if err != nil { - t.Error("Expected no error when till equals currentRBSince, got:", err) - } - - if !fetchCalled { - t.Error("Fetcher should have been called when till equals currentRBSince") - } + assert.Nil(t, err) + assert.NotNil(t, result) + assert.Equal(t, int64(100), result.NewChangeNumber) + assert.Equal(t, int64(150), result.NewRBChangeNumber) + + splitMockFetcher.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) } func TestSynchronizeFeatureFlagsRuleBasedUpdate(t *testing.T) { - // Mock rule based segment storage with testify/mock - ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - - // Mock split storage - splitMockStorage := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return 200, nil }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) {}, - AllCall: func() []dtos.SplitDTO { return []dtos.SplitDTO{} }, - } - - // Mock fetcher - var fetchCalled bool - var fetchCount int - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(requestParams *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - fetchCalled = true - fetchCount++ - if fetchCount == 1 { - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{ - Since: 100, - Till: 200, - Splits: []dtos.SplitDTO{}, + t.Run("Rule-based segment change number lower than current", func(t *testing.T) { + ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} + splitMockStorage := &mocks.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(200), nil).Times(1) + + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + + telemetryMockStorage := mocks.MockTelemetryStorage{ + RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, + RecordSuccessfulSyncCall: func(resource int, timestamp time.Time) {}, + RecordSyncErrorCall: func(resource, status int) {}, + } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Return().Once() + + largeSegmentStorage := &mocks.MockLargeSegmentStorage{} + ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) + + splitUpdater := NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) + + lowerChangeNumber := int64(100) + ruleBasedSegment := &dtos.RuleBasedSegmentDTO{ + Name: "test-segment", + ChangeNumber: lowerChangeNumber, + Conditions: []dtos.RuleBasedConditionDTO{ + { + ConditionType: "WHITELIST", + MatcherGroup: dtos.MatcherGroupDTO{ + Matchers: []dtos.MatcherDTO{}, }, - RuleBasedSegments: dtos.RuleBasedSegmentsDTO{ - Since: 100, - Till: 200, - RuleBasedSegments: []dtos.RuleBasedSegmentDTO{}, - }, - }, nil - } - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{ - Since: 200, - Till: 200, - Splits: []dtos.SplitDTO{}, - }, - RuleBasedSegments: dtos.RuleBasedSegmentsDTO{ - Since: 200, - Till: 200, - RuleBasedSegments: []dtos.RuleBasedSegmentDTO{}, - }, - }, nil - }, - } - - // Mock telemetry storage - telemetryMockStorage := mocks.MockTelemetryStorage{ - RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, - RecordSuccessfulSyncCall: func(resource int, timestamp time.Time) {}, - RecordSyncErrorCall: func(resource, status int) {}, - } - - // Mock app monitor - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) {}, - } - - largeSegmentStorage := &mocks.MockLargeSegmentStorage{} - ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) - - // Create split updater - splitUpdater := NewSplitUpdater( - splitMockStorage, - ruleBasedSegmentMockStorage, - splitMockFetcher, - logging.NewLogger(&logging.LoggerOptions{}), - telemetryMockStorage, - appMonitorMock, - flagsets.NewFlagSetFilter(nil), - ruleBuilder, - ) - - // Test case 1: When rule-based segment change number is lower than current - lowerChangeNumber := int64(100) - ruleBasedSegment := &dtos.RuleBasedSegmentDTO{ - Name: "test-segment", - ChangeNumber: lowerChangeNumber, - Conditions: []dtos.RuleBasedConditionDTO{ - { - ConditionType: "WHITELIST", - MatcherGroup: dtos.MatcherGroupDTO{ - Matchers: []dtos.MatcherDTO{}, }, }, - }, - } - baseMessage := dtos.NewBaseMessage(time.Now().Unix(), "test-channel") - baseUpdate := dtos.NewBaseUpdate(baseMessage, lowerChangeNumber) - ffChange := *dtos.NewRuleBasedSegmentChangeUpdate(baseUpdate, nil, ruleBasedSegment) - - // Reset fetchCalled - fetchCalled = false - - // Set up expectations for the first test case - ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int(200)) - ruleBasedSegmentMockStorage.On("Update", - mock.MatchedBy(func(toAdd []dtos.RuleBasedSegmentDTO) bool { return len(toAdd) == 0 }), - mock.MatchedBy(func(toRemove []dtos.RuleBasedSegmentDTO) bool { return len(toRemove) == 0 }), - int64(200)).Return() - - result, err := splitUpdater.SynchronizeFeatureFlags(&ffChange) - - if err != nil { - t.Error("Expected no error, got:", err) - } - - if result.RequiresFetch { - t.Error("Expected RequiresFetch to be false when change number is lower than current") - } - - if fetchCalled { - t.Error("Fetcher should not have been called when change number is lower than current") - } - - // Test case 2: When rule-based segment change number is higher than current - higherChangeNumber := int64(300) - ruleBasedSegment = &dtos.RuleBasedSegmentDTO{ - Name: "test-segment", - ChangeNumber: higherChangeNumber, - Conditions: []dtos.RuleBasedConditionDTO{ - { - ConditionType: "WHITELIST", - MatcherGroup: dtos.MatcherGroupDTO{ - Matchers: []dtos.MatcherDTO{}, + } + baseMessage := dtos.NewBaseMessage(time.Now().Unix(), "test-channel") + baseUpdate := dtos.NewBaseUpdate(baseMessage, lowerChangeNumber) + ffChange := *dtos.NewRuleBasedSegmentChangeUpdate(baseUpdate, nil, ruleBasedSegment) + + ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(200)).Times(2) + + result, err := splitUpdater.SynchronizeFeatureFlags(&ffChange) + assert.Nil(t, err) + assert.False(t, result.RequiresFetch) + splitMockFetcher.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + }) + + t.Run("Rule-based segment change number higher than current", func(t *testing.T) { + ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} + splitMockStorage := &mocks.SplitStorageMock{} + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + + telemetryMockStorage := mocks.MockTelemetryStorage{ + RecordSyncLatencyCall: func(resource int, latency time.Duration) {}, + RecordSuccessfulSyncCall: func(resource int, timestamp time.Time) {}, + RecordSyncErrorCall: func(resource, status int) {}, + } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + + largeSegmentStorage := &mocks.MockLargeSegmentStorage{} + ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) + + splitUpdater := NewSplitUpdater(splitMockStorage, ruleBasedSegmentMockStorage, splitMockFetcher, logging.NewLogger(&logging.LoggerOptions{}), telemetryMockStorage, appMonitorMock, flagsets.NewFlagSetFilter(nil), ruleBuilder) + + changeNumber := int64(300) + ruleBasedSegment := &dtos.RuleBasedSegmentDTO{ + Name: "test-segment", + ChangeNumber: changeNumber, + Conditions: []dtos.RuleBasedConditionDTO{ + { + ConditionType: "WHITELIST", + MatcherGroup: dtos.MatcherGroupDTO{ + Matchers: []dtos.MatcherDTO{}, + }, }, }, - }, - } - baseMessage = dtos.NewBaseMessage(time.Now().Unix(), "test-channel") - baseUpdate = dtos.NewBaseUpdate(baseMessage, higherChangeNumber) - ffChange = *dtos.NewRuleBasedSegmentChangeUpdate(baseUpdate, nil, ruleBasedSegment) - - // Reset fetchCalled - fetchCalled = false - - // Set up expectations for the second test case - ruleBasedSegmentMockStorage = &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int(100)) - ruleBasedSegmentMockStorage.On("Update", - mock.MatchedBy(func(toAdd []dtos.RuleBasedSegmentDTO) bool { - return len(toAdd) == 1 && toAdd[0].ChangeNumber == higherChangeNumber - }), - mock.MatchedBy(func(toRemove []dtos.RuleBasedSegmentDTO) bool { return len(toRemove) == 0 }), - higherChangeNumber).Return() - - // Create a new split updater for the second test case - splitUpdater = NewSplitUpdater( - splitMockStorage, - ruleBasedSegmentMockStorage, - splitMockFetcher, - logging.NewLogger(&logging.LoggerOptions{}), - telemetryMockStorage, - appMonitorMock, - flagsets.NewFlagSetFilter(nil), - ruleBuilder, - ) - - result, err = splitUpdater.SynchronizeFeatureFlags(&ffChange) - - if err != nil { - t.Error("Expected no error, got:", err) - } - - if result.RequiresFetch { - t.Error("Expected RequiresFetch to be false when change number is higher than current") - } - - if fetchCalled { - t.Error("Fetcher should not have been called when change number is higher than current") - } - - if result.NewChangeNumber != higherChangeNumber { - t.Errorf("Expected NewChangeNumber to be %d, got %d", higherChangeNumber, result.NewChangeNumber) - } - - // Verify that the rule-based segment storage was updated with the higher change number - ruleBasedSegmentMockStorage.AssertCalled(t, "Update", mock.Anything, mock.Anything, higherChangeNumber) + } + baseMessage := dtos.NewBaseMessage(time.Now().Unix(), "test-channel") + baseUpdate := dtos.NewBaseUpdate(baseMessage, changeNumber) + ffChange := *dtos.NewRuleBasedSegmentChangeUpdate(baseUpdate, nil, ruleBasedSegment) + + ruleBasedSegmentMockStorage.On("ChangeNumber").Return(int64(200)).Once() + ruleBasedSegmentMockStorage.On("Update", []dtos.RuleBasedSegmentDTO{*ruleBasedSegment}, []dtos.RuleBasedSegmentDTO{}, changeNumber).Return().Once() + + result, err := splitUpdater.SynchronizeFeatureFlags(&ffChange) + assert.Nil(t, err) + assert.False(t, result.RequiresFetch) + assert.Equal(t, changeNumber, result.NewRBChangeNumber) + + splitMockFetcher.AssertExpectations(t) + splitMockStorage.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + }) } func TestProcessMatchers(t *testing.T) { @@ -1267,7 +776,7 @@ func TestProcessMatchers(t *testing.T) { largeSegmentStorage := &mocks.MockLargeSegmentStorage{} ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, largeSegmentStorage, syncProxyFeatureFlagsRules, syncProxyRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) - splitUpdater := NewSplitUpdater(mocks.MockSplitStorage{}, ruleBasedSegmentMockStorage, fetcherMock.MockSplitFetcher{}, logging.NewLogger(nil), mocks.MockTelemetryStorage{}, hcMock.MockApplicationMonitor{}, flagsets.NewFlagSetFilter(nil), ruleBuilder) + splitUpdater := NewSplitUpdater(mocks.MockSplitStorage{}, ruleBasedSegmentMockStorage, &fetcherMock.MockSplitFetcher{}, logging.NewLogger(nil), mocks.MockTelemetryStorage{}, hcMock.MockApplicationMonitor{}, flagsets.NewFlagSetFilter(nil), ruleBuilder) splitChange := &dtos.SplitChangesDTO{ FeatureFlags: dtos.FeatureFlagsDTO{Till: 1, Since: 1, Splits: []dtos.SplitDTO{ { @@ -1302,18 +811,8 @@ func TestProcessMatchers(t *testing.T) { }, }}} toAdd, _ := splitUpdater.processFeatureFlagChanges(splitChange) - - if toAdd[0].Conditions[0].ConditionType != grammar.ConditionTypeWhitelist { - t.Error("ConditionType should be WHITELIST") - } - if toAdd[0].Conditions[0].MatcherGroup.Matchers[0].MatcherType != grammar.MatcherTypeAllKeys { - t.Error("MatcherType should be ALL_KEYS") - } - - if toAdd[1].Conditions[0].ConditionType != grammar.ConditionTypeRollout { - t.Error("ConditionType should be ROLLOUT") - } - if toAdd[1].Conditions[0].MatcherGroup.Matchers[0].MatcherType != grammar.MatcherTypeAllKeys { - t.Error("MatcherType should be ALL_KEYS") - } + assert.Equal(t, grammar.ConditionTypeWhitelist, toAdd[0].Conditions[0].ConditionType) + assert.Equal(t, grammar.MatcherTypeAllKeys, toAdd[0].Conditions[0].MatcherGroup.Matchers[0].MatcherType) + assert.Equal(t, grammar.ConditionTypeRollout, toAdd[1].Conditions[0].ConditionType) + assert.Equal(t, grammar.MatcherTypeAllKeys, toAdd[1].Conditions[0].MatcherGroup.Matchers[0].MatcherType) } diff --git a/tasks/largesegmentsync_test.go b/tasks/largesegmentsync_test.go index df860b1b..58e2ef76 100644 --- a/tasks/largesegmentsync_test.go +++ b/tasks/largesegmentsync_test.go @@ -1,56 +1,41 @@ package tasks import ( - "sync/atomic" "testing" "time" hcMock "github.com/splitio/go-split-commons/v7/healthcheck/mocks" "github.com/splitio/go-split-commons/v7/storage/mocks" syncMocks "github.com/splitio/go-split-commons/v7/synchronizer/mocks" + "github.com/splitio/go-toolkit/v5/datastructures/set" "github.com/splitio/go-toolkit/v5/logging" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestLargeSegmentSyncTaskHappyPath(t *testing.T) { - var updater syncMocks.LargeSegmentUpdaterMock + updater := &syncMocks.LargeSegmentUpdaterMock{} updater.On("SynchronizeLargeSegment", "ls1", (*int64)(nil)).Return(nil).Once() updater.On("SynchronizeLargeSegment", "ls2", (*int64)(nil)).Return(nil).Once() updater.On("SynchronizeLargeSegment", "ls3", (*int64)(nil)).Return(nil).Once() - var lsNamesCall int64 - splitSorage := mocks.MockSplitStorage{ - LargeSegmentNamesCall: func() *set.ThreadUnsafeSet { - atomic.AddInt64(&lsNamesCall, 1) - return set.NewSet("ls1", "ls2", "ls3") - }, - } - - var notifyEventCalled int64 - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } - task := NewFetchLargeSegmentsTask(&updater, splitSorage, 1, 10, 10, logging.NewLogger(&logging.LoggerOptions{}), appMonitorMock) + splitSorage := &mocks.SplitStorageMock{} + splitSorage.On("LargeSegmentNames").Return(set.NewSet("ls1", "ls2", "ls3")).Once() + + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything) + task := NewFetchLargeSegmentsTask(updater, splitSorage, 2, 10, 10, logging.NewLogger(&logging.LoggerOptions{}), appMonitorMock) task.Start() time.Sleep(3 * time.Second) - if !task.IsRunning() { - t.Error("Large Segment fetching task should be running") - } + assert.True(t, task.IsRunning(), "Large Segment fetching task should be running") task.Stop(true) - if task.IsRunning() { - t.Error("Large Segment fetching task should be stopped") - } - - if lsNamesCall != 2 { - t.Error("Large Segment Call should be 2. Actual: ", lsNamesCall) - } - if atomic.LoadInt64(¬ifyEventCalled) < 1 { - t.Error("It should be called at least once") - } + assert.False(t, task.IsRunning(), "Large Segment fetching task should be stopped") + splitSorage.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) updater.AssertExpectations(t) } diff --git a/tasks/splitsync_test.go b/tasks/splitsync_test.go index 8cb304bc..8481d04f 100644 --- a/tasks/splitsync_test.go +++ b/tasks/splitsync_test.go @@ -1,8 +1,6 @@ package tasks import ( - "net/http" - "sync/atomic" "testing" "time" @@ -10,12 +8,13 @@ import ( "github.com/splitio/go-split-commons/v7/engine/grammar" "github.com/splitio/go-split-commons/v7/flagsets" hcMock "github.com/splitio/go-split-commons/v7/healthcheck/mocks" - "github.com/splitio/go-split-commons/v7/service" fetcherMock "github.com/splitio/go-split-commons/v7/service/mocks" "github.com/splitio/go-split-commons/v7/storage/mocks" "github.com/splitio/go-split-commons/v7/synchronizer/worker/split" "github.com/splitio/go-split-commons/v7/telemetry" "github.com/splitio/go-toolkit/v5/logging" + + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -29,58 +28,20 @@ var goClientRuleBasedSegmentRules = []string{grammar.MatcherTypeAllKeys, grammar grammar.MatcherTypeInRuleBasedSegment} func TestSplitSyncTask(t *testing.T) { - var call int64 - var notifyEventCalled int64 - mockedSplit1 := dtos.SplitDTO{Name: "split1", Killed: false, Status: "ACTIVE", TrafficTypeName: "one"} mockedSplit2 := dtos.SplitDTO{Name: "split2", Killed: true, Status: "ACTIVE", TrafficTypeName: "two"} mockedSplit3 := dtos.SplitDTO{Name: "split3", Killed: true, Status: "INACTIVE", TrafficTypeName: "one"} - splitMockStorage := mocks.MockSplitStorage{ - ChangeNumberCall: func() (int64, error) { return -1, nil }, - UpdateCall: func(toAdd []dtos.SplitDTO, toRemove []dtos.SplitDTO, changeNumber int64) { - if changeNumber != 3 { - t.Error("Wrong changenumber") - } - if len(toAdd) != 2 { - t.Error("Wrong length of passed splits") - } - s1 := toAdd[0] - if s1.Name != "split1" || s1.Killed { - t.Error("split1 stored/retrieved incorrectly") - t.Error(s1) - } - s2 := toAdd[1] - if s2.Name != "split2" || !s2.Killed { - t.Error("split2 stored/retrieved incorrectly") - t.Error(s2) - } - }, - RemoveCall: func(splitname string) { - if splitname != "split3" { - t.Error("It should remove split3") - } - }, - } + splitMockStorage := &mocks.SplitStorageMock{} + splitMockStorage.On("ChangeNumber").Return(int64(-1), nil) + splitMockStorage.On("Update", []dtos.SplitDTO{mockedSplit1, mockedSplit2}, []dtos.SplitDTO{mockedSplit3}, int64(3)).Once() - splitMockFetcher := fetcherMock.MockSplitFetcher{ - FetchCall: func(fetchOptions *service.FlagRequestParams) (*dtos.SplitChangesDTO, error) { - atomic.AddInt64(&call, 1) - req, _ := http.NewRequest("GET", "test", nil) - fetchOptions.Apply(req) - if req.Header.Get("Cache-Control") != "no-cache" { - t.Error("Wrong header") - } - if req.URL.Query().Get("since") != "-1" { - t.Error("Wrong since") - } - return &dtos.SplitChangesDTO{ - FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2, mockedSplit3}, - Since: 3, - Till: 3}, - }, nil - }, - } + splitMockFetcher := &fetcherMock.MockSplitFetcher{} + splitMockFetcher.On("Fetch", mock.Anything).Return(&dtos.SplitChangesDTO{ + FeatureFlags: dtos.FeatureFlagsDTO{Splits: []dtos.SplitDTO{mockedSplit1, mockedSplit2, mockedSplit3}, + Since: 3, + Till: 3}, + }, nil).Once() telemetryMockStorage := mocks.MockTelemetryStorage{ RecordSuccessfulSyncCall: func(resource int, tm time.Time) { @@ -95,14 +56,11 @@ func TestSplitSyncTask(t *testing.T) { }, } - appMonitorMock := hcMock.MockApplicationMonitor{ - NotifyEventCall: func(counterType int) { - atomic.AddInt64(¬ifyEventCalled, 1) - }, - } + appMonitorMock := &hcMock.ApplicationMonitorMock{} + appMonitorMock.On("NotifyEvent", mock.Anything).Once() ruleBasedSegmentMockStorage := &mocks.MockRuleBasedSegmentStorage{} - ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(-1) + ruleBasedSegmentMockStorage.On("ChangeNumber").Twice().Return(int64(-1)) ruleBasedSegmentMockStorage.On("Update", mock.Anything, mock.Anything, mock.Anything).Once().Return(-1) ruleBuilder := grammar.NewRuleBuilder(nil, ruleBasedSegmentMockStorage, nil, goClientFeatureFlagsRules, goClientRuleBasedSegmentRules, logging.NewLogger(&logging.LoggerOptions{}), nil) @@ -116,19 +74,13 @@ func TestSplitSyncTask(t *testing.T) { splitTask.Start() time.Sleep(2 * time.Second) - if !splitTask.IsRunning() { - t.Error("Split fetching task should be running") - } + assert.True(t, splitTask.IsRunning(), "Split fetching task should be running") splitTask.Stop(false) - if atomic.LoadInt64(&call) < 1 { - t.Error("Request not received") - } + assert.False(t, splitTask.IsRunning(), "Split fetching task should be stopped") - if splitTask.IsRunning() { - t.Error("Task should be stopped") - } - if atomic.LoadInt64(¬ifyEventCalled) < 1 { - t.Error("It should be called at least once") - } + splitMockStorage.AssertExpectations(t) + splitMockFetcher.AssertExpectations(t) + appMonitorMock.AssertExpectations(t) + ruleBasedSegmentMockStorage.AssertExpectations(t) }