X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=internal%2Fsdlgoredis%2Fsdlgoredis_test.go;h=c95b8afbdc9790bd28cee286e3dd7b1dbf37770b;hb=refs%2Ftags%2Fv0.9.6;hp=34e057f0d8265ae183b728f6373b0cdd2d5bceaa;hpb=6f724aa3950cdc01246351644348fdc0f6e9ca4a;p=ric-plt%2Fsdlgo.git diff --git a/internal/sdlgoredis/sdlgoredis_test.go b/internal/sdlgoredis/sdlgoredis_test.go index 34e057f..c95b8af 100644 --- a/internal/sdlgoredis/sdlgoredis_test.go +++ b/internal/sdlgoredis/sdlgoredis_test.go @@ -23,9 +23,10 @@ package sdlgoredis_test import ( + "context" "errors" "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/sdlgoredis" - "github.com/go-redis/redis/v7" + "github.com/go-redis/redis/v8" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "strconv" @@ -45,15 +46,15 @@ type MockOS struct { mock.Mock } -func (m *pubSubMock) Channel() <-chan *redis.Message { +func (m *pubSubMock) Channel(opts ...redis.ChannelOption) <-chan *redis.Message { return m.Called().Get(0).(chan *redis.Message) } -func (m *pubSubMock) Subscribe(channels ...string) error { +func (m *pubSubMock) Subscribe(ctx context.Context, channels ...string) error { return m.Called().Error(0) } -func (m *pubSubMock) Unsubscribe(channels ...string) error { +func (m *pubSubMock) Unsubscribe(ctx context.Context, channels ...string) error { return m.Called().Error(0) } @@ -61,7 +62,7 @@ func (m *pubSubMock) Close() error { return m.Called().Error(0) } -func (m *clientMock) Command() *redis.CommandsInfoCmd { +func (m *clientMock) Command(ctx context.Context) *redis.CommandsInfoCmd { return m.Called().Get(0).(*redis.CommandsInfoCmd) } @@ -69,75 +70,75 @@ func (m *clientMock) Close() error { return m.Called().Error(0) } -func (m *clientMock) Subscribe(channels ...string) *redis.PubSub { +func (m *clientMock) Subscribe(ctx context.Context, channels ...string) *redis.PubSub { return m.Called(channels).Get(0).(*redis.PubSub) } -func (m *clientMock) MSet(pairs ...interface{}) *redis.StatusCmd { +func (m *clientMock) MSet(ctx context.Context, pairs ...interface{}) *redis.StatusCmd { return m.Called(pairs).Get(0).(*redis.StatusCmd) } -func (m *clientMock) Do(args ...interface{}) *redis.Cmd { +func (m *clientMock) Do(ctx context.Context, args ...interface{}) *redis.Cmd { return m.Called(args).Get(0).(*redis.Cmd) } -func (m *clientMock) MGet(keys ...string) *redis.SliceCmd { +func (m *clientMock) MGet(ctx context.Context, keys ...string) *redis.SliceCmd { return m.Called(keys).Get(0).(*redis.SliceCmd) } -func (m *clientMock) Del(keys ...string) *redis.IntCmd { +func (m *clientMock) Del(ctx context.Context, keys ...string) *redis.IntCmd { return m.Called(keys).Get(0).(*redis.IntCmd) } -func (m *clientMock) Keys(pattern string) *redis.StringSliceCmd { +func (m *clientMock) Keys(ctx context.Context, pattern string) *redis.StringSliceCmd { return m.Called(pattern).Get(0).(*redis.StringSliceCmd) } -func (m *clientMock) SetNX(key string, value interface{}, expiration time.Duration) *redis.BoolCmd { +func (m *clientMock) SetNX(ctx context.Context, key string, value interface{}, expiration time.Duration) *redis.BoolCmd { return m.Called(key, value, expiration).Get(0).(*redis.BoolCmd) } -func (m *clientMock) SAdd(key string, members ...interface{}) *redis.IntCmd { +func (m *clientMock) SAdd(ctx context.Context, key string, members ...interface{}) *redis.IntCmd { return m.Called(key, members).Get(0).(*redis.IntCmd) } -func (m *clientMock) SRem(key string, members ...interface{}) *redis.IntCmd { +func (m *clientMock) SRem(ctx context.Context, key string, members ...interface{}) *redis.IntCmd { return m.Called(key, members).Get(0).(*redis.IntCmd) } -func (m *clientMock) SMembers(key string) *redis.StringSliceCmd { +func (m *clientMock) SMembers(ctx context.Context, key string) *redis.StringSliceCmd { return m.Called(key).Get(0).(*redis.StringSliceCmd) } -func (m *clientMock) SIsMember(key string, member interface{}) *redis.BoolCmd { +func (m *clientMock) SIsMember(ctx context.Context, key string, member interface{}) *redis.BoolCmd { return m.Called(key, member).Get(0).(*redis.BoolCmd) } -func (m *clientMock) SCard(key string) *redis.IntCmd { +func (m *clientMock) SCard(ctx context.Context, key string) *redis.IntCmd { return m.Called(key).Get(0).(*redis.IntCmd) } -func (m *clientMock) PTTL(key string) *redis.DurationCmd { +func (m *clientMock) PTTL(ctx context.Context, key string) *redis.DurationCmd { return m.Called(key).Get(0).(*redis.DurationCmd) } -func (m *clientMock) Eval(script string, keys []string, args ...interface{}) *redis.Cmd { +func (m *clientMock) Eval(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd { return m.Called(script, keys).Get(0).(*redis.Cmd) } -func (m *clientMock) EvalSha(sha1 string, keys []string, args ...interface{}) *redis.Cmd { +func (m *clientMock) EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *redis.Cmd { return m.Called(sha1, keys, args).Get(0).(*redis.Cmd) } -func (m *clientMock) ScriptExists(scripts ...string) *redis.BoolSliceCmd { +func (m *clientMock) ScriptExists(ctx context.Context, scripts ...string) *redis.BoolSliceCmd { return m.Called(scripts).Get(0).(*redis.BoolSliceCmd) } -func (m *clientMock) ScriptLoad(script string) *redis.StringCmd { +func (m *clientMock) ScriptLoad(ctx context.Context, script string) *redis.StringCmd { return m.Called(script).Get(0).(*redis.StringCmd) } -func (m *clientMock) Info(section ...string) *redis.StringCmd { +func (m *clientMock) Info(ctx context.Context, section ...string) *redis.StringCmd { return m.Called(section).Get(0).(*redis.StringCmd) } @@ -145,24 +146,24 @@ type MockRedisSentinel struct { mock.Mock } -func (m *MockRedisSentinel) Master(name string) *redis.StringStringMapCmd { +func (m *MockRedisSentinel) Master(ctx context.Context, name string) *redis.StringStringMapCmd { a := m.Called(name) return a.Get(0).(*redis.StringStringMapCmd) } -func (m *MockRedisSentinel) Slaves(name string) *redis.SliceCmd { +func (m *MockRedisSentinel) Slaves(ctx context.Context, name string) *redis.SliceCmd { a := m.Called(name) return a.Get(0).(*redis.SliceCmd) } -func (m *MockRedisSentinel) Sentinels(name string) *redis.SliceCmd { +func (m *MockRedisSentinel) Sentinels(ctx context.Context, name string) *redis.SliceCmd { a := m.Called(name) return a.Get(0).(*redis.SliceCmd) } func setSubscribeNotifications() (*pubSubMock, sdlgoredis.SubscribeFn) { mock := new(pubSubMock) - return mock, func(client sdlgoredis.RedisClient, channels ...string) sdlgoredis.Subscriber { + return mock, func(ctx context.Context, client sdlgoredis.RedisClient, channels ...string) sdlgoredis.Subscriber { return mock } } @@ -899,7 +900,7 @@ func TestSubscribeChannelDBSubscribeRXUnsubscribe(t *testing.T) { ps, r, db := setupHaEnv(true) ch := make(chan *redis.Message) msg := redis.Message{ - Channel: "{prefix}channel", + Channel: "{prefix},channel", Pattern: "pattern", Payload: "event", } @@ -911,9 +912,9 @@ func TestSubscribeChannelDBSubscribeRXUnsubscribe(t *testing.T) { db.SubscribeChannelDB(func(channel string, payload ...string) { count++ receivedChannel = channel - }, "{prefix}", "---", "{prefix}channel") + }, "{prefix},channel") ch <- &msg - db.UnsubscribeChannelDB("{prefix}channel") + db.UnsubscribeChannelDB("{prefix},channel") time.Sleep(1 * time.Second) assert.Equal(t, 1, count) assert.Equal(t, "channel", receivedChannel) @@ -925,12 +926,12 @@ func TestSubscribeChannelDBSubscribeTwoUnsubscribeOne(t *testing.T) { ps, r, db := setupHaEnv(true) ch := make(chan *redis.Message) msg1 := redis.Message{ - Channel: "{prefix}channel1", + Channel: "{prefix},channel1", Pattern: "pattern", Payload: "event", } msg2 := redis.Message{ - Channel: "{prefix}channel2", + Channel: "{prefix},channel2", Pattern: "pattern", Payload: "event", } @@ -944,18 +945,18 @@ func TestSubscribeChannelDBSubscribeTwoUnsubscribeOne(t *testing.T) { db.SubscribeChannelDB(func(channel string, payload ...string) { count++ receivedChannel1 = channel - }, "{prefix}", "---", "{prefix}channel1") + }, "{prefix},channel1") ch <- &msg1 receivedChannel2 := "" db.SubscribeChannelDB(func(channel string, payload ...string) { count++ receivedChannel2 = channel - }, "{prefix}", "---", "{prefix}channel2") + }, "{prefix},channel2") time.Sleep(1 * time.Second) - db.UnsubscribeChannelDB("{prefix}channel1") + db.UnsubscribeChannelDB("{prefix},channel1") ch <- &msg2 - db.UnsubscribeChannelDB("{prefix}channel2") + db.UnsubscribeChannelDB("{prefix},channel2") time.Sleep(1 * time.Second) assert.Equal(t, 2, count) assert.Equal(t, "channel1", receivedChannel1) @@ -964,11 +965,54 @@ func TestSubscribeChannelDBSubscribeTwoUnsubscribeOne(t *testing.T) { ps.AssertExpectations(t) } +func TestSubscribeChannelDBTwoSubscribesWithUnequalPrefixAndUnsubscribes(t *testing.T) { + ps, r, db := setupHaEnv(true) + ch := make(chan *redis.Message) + msg1 := redis.Message{ + Channel: "{prefix1},channel", + Pattern: "pattern", + Payload: "event", + } + msg2 := redis.Message{ + Channel: "{prefix2},channel", + Pattern: "pattern", + Payload: "event", + } + ps.On("Channel").Return(ch) + ps.On("Subscribe").Return(nil) + ps.On("Unsubscribe").Return(nil) + ps.On("Unsubscribe").Return(nil) + ps.On("Close").Return(nil) + count := 0 + receivedChannel1 := "" + db.SubscribeChannelDB(func(channel string, payload ...string) { + count++ + receivedChannel1 = channel + }, "{prefix1},channel") + ch <- &msg1 + receivedChannel2 := "" + db.SubscribeChannelDB(func(channel string, payload ...string) { + count++ + receivedChannel2 = channel + }, "{prefix2},channel") + + time.Sleep(1 * time.Second) + db.UnsubscribeChannelDB("{prefix1},channel") + ch <- &msg2 + db.UnsubscribeChannelDB("{prefix2},channel") + time.Sleep(1 * time.Second) + assert.Equal(t, 2, count) + assert.Equal(t, "channel", receivedChannel1) + assert.Equal(t, "channel", receivedChannel2) + r.AssertExpectations(t) + ps.AssertExpectations(t) +} + func TestSubscribeChannelReDBSubscribeAfterUnsubscribe(t *testing.T) { ps, r, db := setupHaEnv(true) ch := make(chan *redis.Message) msg := redis.Message{ - Channel: "{prefix}channel", + Channel: "{prefix},channel", Pattern: "pattern", Payload: "event", } @@ -981,17 +1025,17 @@ func TestSubscribeChannelReDBSubscribeAfterUnsubscribe(t *testing.T) { db.SubscribeChannelDB(func(channel string, payload ...string) { count++ receivedChannel = channel - }, "{prefix}", "---", "{prefix}channel") + }, "{prefix},channel") ch <- &msg - db.UnsubscribeChannelDB("{prefix}channel") + db.UnsubscribeChannelDB("{prefix},channel") time.Sleep(1 * time.Second) db.SubscribeChannelDB(func(channel string, payload ...string) { count++ receivedChannel = channel - }, "{prefix}", "---", "{prefix}channel") + }, "{prefix}", "---", "{prefix},channel") ch <- &msg - db.UnsubscribeChannelDB("{prefix}channel") + db.UnsubscribeChannelDB("{prefix},channel") time.Sleep(1 * time.Second) assert.Equal(t, 2, count) @@ -1000,6 +1044,32 @@ func TestSubscribeChannelReDBSubscribeAfterUnsubscribe(t *testing.T) { ps.AssertExpectations(t) } +func TestSubscribeChannelDBSubscribeReceivedEventIgnoredIfChannelNameIsUnknown(t *testing.T) { + ps, r, db := setupHaEnv(true) + ch := make(chan *redis.Message) + msg := redis.Message{ + Channel: "missingNsPrefixchannel", + Pattern: "pattern", + Payload: "event", + } + ps.On("Channel").Return(ch) + ps.On("Unsubscribe").Return(nil) + ps.On("Close").Return(nil) + count := 0 + receivedChannel := "" + db.SubscribeChannelDB(func(channel string, payload ...string) { + count++ + receivedChannel = channel + }, "{prefix},channel") + ch <- &msg + db.UnsubscribeChannelDB("{prefix},channel") + time.Sleep(1 * time.Second) + assert.Equal(t, 0, count) + assert.Equal(t, "", receivedChannel) + r.AssertExpectations(t) + ps.AssertExpectations(t) +} + func TestPTTLSuccessfully(t *testing.T) { _, r, db := setupHaEnv(true) expectedKey := "key" @@ -1207,7 +1277,6 @@ func TestInfoOfStandalonePrimaryRedisSuccessfully(t *testing.T) { } func TestInfoOfStandalonePrimaryRedisFailureWhenIntConversionFails(t *testing.T) { - expErr := errors.New("Info reply error: strconv.ParseUint: parsing \"not-int\": invalid syntax") _, r, db := setupHaEnv(true) redisInfo := "# Replication\r\n" + "role:master\r\n" + @@ -1222,7 +1291,7 @@ func TestInfoOfStandalonePrimaryRedisFailureWhenIntConversionFails(t *testing.T) r.On("Info", []string{"all"}).Return(redis.NewStringResult(redisInfo, nil)) info, err := db.Info() - assert.Equal(t, expErr, err) + assert.Nil(t, err) assert.Equal(t, expInfo, info) r.AssertExpectations(t) } @@ -1255,6 +1324,71 @@ func TestInfoWithEmptyContentSuccessfully(t *testing.T) { r.AssertExpectations(t) } +func TestInfoWithSomeStatisticsOfStandalonePrimaryRedis(t *testing.T) { + _, r, db := setupHaEnv(true) + redisInfo := "# Replication\r\n" + + "role:master\r\n" + + "connected_slaves:0\r\n" + + "min_slaves_good_slaves:0\r\n" + + "# Server\r\n" + + "uptime_in_days:23\r\n" + + "# Clients\r\n" + + "connected_clients:2\r\n" + + "# Memory\r\n" + + "used_memory:2093808\r\n" + + "used_memory_human:2.00M\r\n" + + "mem_fragmentation_ratio:6.44\r\n" + + "# Stats\r\n" + + "total_connections_received:278\r\n" + + "# CPU\r\n" + + "used_cpu_sys:1775.254919\r\n" + + "# Commandstats\r\n" + + "cmdstat_role:calls=1,usec=3,usec_per_call=3.00\r\n" + + "# Keyspace\r\n" + + "db0:keys=4,expires=0,avg_ttl=0" + expInfo := &sdlgoredis.DbInfo{ + Fields: sdlgoredis.DbInfoFields{ + PrimaryRole: true, + ConnectedReplicaCnt: 0, + Server: sdlgoredis.ServerInfoFields{ + UptimeInDays: 23, + }, + Clients: sdlgoredis.ClientsInfoFields{ + ConnectedClients: 2, + }, + Memory: sdlgoredis.MeroryInfoFields{ + UsedMemory: 2093808, + UsedMemoryHuman: "2.00M", + MemFragmentationRatio: 6.44, + }, + Stats: sdlgoredis.StatsInfoFields{ + TotalConnectionsReceived: 278, + }, + Cpu: sdlgoredis.CpuInfoFields{ + UsedCpuSys: 1775.254919, + }, + Commandstats: sdlgoredis.CommandstatsInfoFields{ + CmdstatRole: sdlgoredis.CommandstatsValues{ + Calls: 1, + Usec: 3, + UsecPerCall: 3.00, + }, + }, + Keyspace: sdlgoredis.KeyspaceInfoFields{ + Db: sdlgoredis.KeyspaceValues{ + Keys: 4, + }, + }, + }, + } + + r.On("Info", []string{"all"}).Return(redis.NewStringResult(redisInfo, nil)) + info, err := db.Info() + assert.Nil(t, err) + assert.Equal(t, expInfo, info) + r.AssertExpectations(t) +} + func TestStateWithPrimaryAndTwoReplicaRedisSuccessfully(t *testing.T) { _, r, s, db := setupHaEnvWithSentinels(true, "3") @@ -1390,6 +1524,34 @@ func TestStateWithPrimaryAndTwoReplicaRedisFailureWhenIntConversionFails(t *test r.AssertExpectations(t) } +// Test case to test ignoring of a sentinel entry with zero port. Implementation has been +// done because we miss the fix for the Redis Bug #9240. +func TestStateWithPrimaryAndTwoReplicaFirstSentinelStateIgnoredBecauseZeroPortBugRedisSuccessfully(t *testing.T) { + _, r, s, db := setupHaEnvWithSentinels(true, "3") + + redisPrimaryState := newMockRedisMasterCallResp("master", "10.20.30.30", "6379", "master") + redisReplicasState := newMockRedisSlavesCall() + redisReplicasState.add("slave", "10.20.30.40", "6379", "up", "slave") + redisReplicasState.add("slave", "10.20.30.50", "30000", "up", "slave") + redisSentinelsState := newMockRedisSentinelsCall() + redisSentinelsState.add("10.20.30.40", "0", "s_down,sentinel,disconnected") + redisSentinelsState.add("10.20.30.50", "26379", "sentinel") + + expState := newExpDbState(3, nil) + expState.addPrimary("master", "10.20.30.30", "6379", "master", nil) + expState.addReplica("slave", "10.20.30.40", "6379", "up", "slave", nil) + expState.addReplica("slave", "10.20.30.50", "30000", "up", "slave", nil) + expState.addSentinel("10.20.30.50", "26379", "sentinel", nil) + + s[0].On("Master", "dbaasmaster").Return(redis.NewStringStringMapResult(redisPrimaryState, nil)) + s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisReplicasState.resp, nil)) + s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState.resp, nil)) + ret, err := db.State() + assert.Nil(t, err) + assert.Equal(t, expState.s, *ret) + r.AssertExpectations(t) +} + func TestStateWithSinglePrimaryRedisSuccessfully(t *testing.T) { _, r, db := setupSingleEnv(true, "1") redisInfo := "# Replication\r\n" + @@ -1402,6 +1564,8 @@ func TestStateWithSinglePrimaryRedisSuccessfully(t *testing.T) { PrimaryDbState: sdlgoredis.PrimaryDbState{ Fields: sdlgoredis.PrimaryDbStateFields{ Role: "master", + Ip: "service-ricplt-dbaas-tcp-cluster-0.ricplt", + Port: "6376", Flags: "master", }, }, @@ -1428,6 +1592,8 @@ func TestStateWithSinglePrimaryRedisFailureWhenIntConversionFails(t *testing.T) PrimaryDbState: sdlgoredis.PrimaryDbState{ Fields: sdlgoredis.PrimaryDbStateFields{ Role: "master", + Ip: "service-ricplt-dbaas-tcp-cluster-0.ricplt", + Port: "6376", Flags: "master", }, }, @@ -1456,3 +1622,36 @@ func TestStateWithSinglePrimaryRedisFailureInInfoCall(t *testing.T) { assert.Equal(t, expState, ret) r.AssertExpectations(t) } + +func TestStatisticsWithSinglePrimaryRedisSuccessfully(t *testing.T) { + _, r, db := setupSingleEnv(true, "1") + redisInfo := "# Replication\r\n" + + "role:master\r\n" + + "connected_slaves:0\r\n" + + "min_slaves_good_slaves:0\r\n" + + "# Server\r\n" + + "uptime_in_days:12\r\n" + + expStatistics := &sdlgoredis.DbStatistics{ + Stats: []*sdlgoredis.DbStatisticsInfo{ + { + IPAddr: "service-ricplt-dbaas-tcp-cluster-0.ricplt", + Port: "6376", + Info: &sdlgoredis.DbInfo{ + Fields: sdlgoredis.DbInfoFields{ + PrimaryRole: true, + Server: sdlgoredis.ServerInfoFields{ + UptimeInDays: 12, + }, + }, + }, + }, + }, + } + + r.On("Info", []string{"all"}).Return(redis.NewStringResult(redisInfo, nil)) + ret, err := db.Statistics() + assert.Nil(t, err) + assert.Equal(t, expStatistics, ret) + r.AssertExpectations(t) +}