Merge "RIC:1060: Change in PTL"
[ric-plt/sdlgo.git] / sdl_test.go
index 3d06bb4..725552a 100644 (file)
    limitations under the License.
 */
 
+/*
+ * This source code is part of the near-RT RIC (RAN Intelligent Controller)
+ * platform project (RICP).
+ */
+
 package sdlgo_test
 
 import (
        "errors"
+       "reflect"
        "testing"
+       "time"
 
        "gerrit.o-ran-sc.org/r/ric-plt/sdlgo"
-       "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/sdlgoredis"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/mock"
 )
@@ -31,12 +37,14 @@ type mockDB struct {
        mock.Mock
 }
 
-func (m *mockDB) SubscribeChannelDB(cb sdlgoredis.ChannelNotificationCb, channelPrefix, eventSeparator string, channels ...string) {
-       m.Called(cb, channelPrefix, eventSeparator, channels)
+func (m *mockDB) SubscribeChannelDB(cb func(string, ...string), channels ...string) error {
+       a := m.Called(cb, channels)
+       return a.Error(0)
 }
 
-func (m *mockDB) UnsubscribeChannelDB(channels ...string) {
-       m.Called(channels)
+func (m *mockDB) UnsubscribeChannelDB(channels ...string) error {
+       a := m.Called(channels)
+       return a.Error(0)
 }
 
 func (m *mockDB) MSet(pairs ...interface{}) error {
@@ -44,8 +52,8 @@ func (m *mockDB) MSet(pairs ...interface{}) error {
        return a.Error(0)
 }
 
-func (m *mockDB) MSetPub(ns, message string, pairs ...interface{}) error {
-       a := m.Called(ns, message, pairs)
+func (m *mockDB) MSetMPub(channelsAndEvents []string, pairs ...interface{}) error {
+       a := m.Called(channelsAndEvents, pairs)
        return a.Error(0)
 }
 
@@ -64,8 +72,8 @@ func (m *mockDB) Del(keys []string) error {
        return a.Error(0)
 }
 
-func (m *mockDB) DelPub(channel, message string, keys []string) error {
-       a := m.Called(channel, message, keys)
+func (m *mockDB) DelMPub(channelsAndEvents []string, keys []string) error {
+       a := m.Called(channelsAndEvents, keys)
        return a.Error(0)
 }
 
@@ -79,18 +87,18 @@ func (m *mockDB) SetIE(key string, oldData, newData interface{}) (bool, error) {
        return a.Bool(0), a.Error(1)
 }
 
-func (m *mockDB) SetIEPub(channel, message, key string, oldData, newData interface{}) (bool, error) {
-       a := m.Called(channel, message, key, oldData, newData)
+func (m *mockDB) SetIEPub(channelsAndEvents []string, key string, oldData, newData interface{}) (bool, error) {
+       a := m.Called(channelsAndEvents, key, oldData, newData)
        return a.Bool(0), a.Error(1)
 }
 
-func (m *mockDB) SetNX(key string, data interface{}) (bool, error) {
-       a := m.Called(key, data)
+func (m *mockDB) SetNX(key string, data interface{}, expiration time.Duration) (bool, error) {
+       a := m.Called(key, data, expiration)
        return a.Bool(0), a.Error(1)
 }
 
-func (m *mockDB) SetNXPub(channel, message, key string, data interface{}) (bool, error) {
-       a := m.Called(channel, message, key, data)
+func (m *mockDB) SetNXPub(channelsAndEvents []string, key string, data interface{}) (bool, error) {
+       a := m.Called(channelsAndEvents, key, data)
        return a.Bool(0), a.Error(1)
 }
 
@@ -99,8 +107,8 @@ func (m *mockDB) DelIE(key string, data interface{}) (bool, error) {
        return a.Bool(0), a.Error(1)
 }
 
-func (m *mockDB) DelIEPub(channel, message, key string, data interface{}) (bool, error) {
-       a := m.Called(channel, message, key, data)
+func (m *mockDB) DelIEPub(channelsAndEvents []string, key string, data interface{}) (bool, error) {
+       a := m.Called(channelsAndEvents, key, data)
        return a.Bool(0), a.Error(1)
 }
 
@@ -128,20 +136,82 @@ func (m *mockDB) SCard(key string) (int64, error) {
        return a.Get(0).(int64), a.Error(1)
 }
 
+func (m *mockDB) PTTL(key string) (time.Duration, error) {
+       a := m.Called(key)
+       return a.Get(0).(time.Duration), a.Error(1)
+}
+
+func (m *mockDB) PExpireIE(key string, data interface{}, expiration time.Duration) error {
+       a := m.Called(key, data, expiration)
+       return a.Error(0)
+}
+
 func setup() (*mockDB, *sdlgo.SdlInstance) {
        m := new(mockDB)
-       i := sdlgo.NewSdlInstance("namespace", m)
+       i := sdlgo.NewSdlInstanceForTest("namespace", m)
        return m, i
 }
 
+func verifySliceInOrder(a, b []string) bool {
+       for i, v := range a {
+               found := false
+               if i%2 == 0 {
+                       for j, x := range b {
+                               if j%2 == 0 && x == v && a[i+1] == b[j+1] {
+                                       found = true
+                                       break
+                               }
+                       }
+                       if !found {
+                               return false
+                       }
+               }
+       }
+       return true
+
+}
+
+func TestClose(t *testing.T) {
+       m, i := setup()
+
+       m.On("CloseDB").Return(nil)
+       err := i.Close()
+       assert.Nil(t, err)
+       m.AssertExpectations(t)
+}
+
+func TestCloseReturnError(t *testing.T) {
+       m, i := setup()
+
+       m.On("CloseDB").Return(errors.New("Some error"))
+       err := i.Close()
+       assert.NotNil(t, err)
+       m.AssertExpectations(t)
+}
+
 func TestSubscribeChannel(t *testing.T) {
        m, i := setup()
 
        expectedCB := func(channel string, events ...string) {}
        expectedChannels := []string{"{namespace},channel1", "{namespace},channel2"}
 
-       m.On("SubscribeChannelDB", mock.AnythingOfType("sdlgoredis.ChannelNotificationCb"), "{namespace},", "___", expectedChannels).Return()
-       i.SubscribeChannel(expectedCB, "channel1", "channel2")
+       m.On("SubscribeChannelDB", mock.AnythingOfType("func(string, ...string)"), expectedChannels).Return(nil)
+       err := i.SubscribeChannel(expectedCB, "channel1", "channel2")
+       assert.Nil(t, err)
+       m.AssertExpectations(t)
+}
+
+func TestSubscribeChannelError(t *testing.T) {
+       mockedErr := errors.New("Some DB Backend Subscribe Error")
+       m, i := setup()
+
+       expectedCB := func(channel string, events ...string) {}
+       expectedChannels := []string{"{namespace},channel1", "{namespace},channel2"}
+
+       m.On("SubscribeChannelDB", mock.AnythingOfType("func(string, ...string)"), expectedChannels).Return(mockedErr)
+       err := i.SubscribeChannel(expectedCB, "channel1", "channel2")
+       assert.NotNil(t, err)
+       assert.Contains(t, err.Error(), mockedErr.Error())
        m.AssertExpectations(t)
 }
 
@@ -150,10 +220,25 @@ func TestUnsubscribeChannel(t *testing.T) {
 
        expectedChannels := []string{"{namespace},channel1", "{namespace},channel2"}
 
-       m.On("UnsubscribeChannelDB", expectedChannels).Return()
-       i.UnsubscribeChannel("channel1", "channel2")
+       m.On("UnsubscribeChannelDB", expectedChannels).Return(nil)
+       err := i.UnsubscribeChannel("channel1", "channel2")
+       assert.Nil(t, err)
+       m.AssertExpectations(t)
+}
+
+func TestUnsubscribeChannelError(t *testing.T) {
+       mockedErr := errors.New("Some DB Backend Unsubscribe Error")
+       m, i := setup()
+
+       expectedChannels := []string{"{namespace},channel1", "{namespace},channel2"}
+
+       m.On("UnsubscribeChannelDB", expectedChannels).Return(mockedErr)
+       err := i.UnsubscribeChannel("channel1", "channel2")
+       assert.NotNil(t, err)
+       assert.Contains(t, err.Error(), mockedErr.Error())
        m.AssertExpectations(t)
 }
+
 func TestGetOneKey(t *testing.T) {
        m, i := setup()
 
@@ -272,6 +357,39 @@ func TestWriteByteArrayAsValue(t *testing.T) {
        m.AssertExpectations(t)
 }
 
+func TestWriteMapAsInput(t *testing.T) {
+       m, i := setup()
+
+       setExpected := []interface{}{"{namespace},key1", "string123",
+               "{namespace},key22", 12,
+               "{namespace},key333", []byte{1, 2, 3, 4, 5}}
+       inputMap := map[string]interface{}{
+               "key1":   "string123",
+               "key22":  12,
+               "key333": []byte{1, 2, 3, 4, 5},
+       }
+
+       m.On("MSet", mock.MatchedBy(func(input []interface{}) bool {
+               for _, v := range input {
+                       found := false
+                       for _, x := range setExpected {
+                               found = reflect.DeepEqual(x, v)
+                               if found == true {
+                                       break
+                               }
+                       }
+                       if found == false {
+                               return false
+                       }
+               }
+               return true
+       })).Return(nil)
+
+       err := i.Set(inputMap)
+       assert.Nil(t, err)
+       m.AssertExpectations(t)
+}
+
 func TestWriteMixed(t *testing.T) {
        m, i := setup()
 
@@ -372,24 +490,41 @@ func TestWriteEmptyList(t *testing.T) {
 func TestWriteAndPublishOneKeyOneChannel(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedMessage := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        expectedKeyVal := []interface{}{"{namespace},key1", "data1"}
 
-       m.On("MSetPub", expectedChannel, expectedMessage, expectedKeyVal).Return(nil)
+       m.On("MSetMPub", expectedChannelAndEvent, expectedKeyVal).Return(nil)
        m.AssertNotCalled(t, "MSet", expectedKeyVal)
        err := i.SetAndPublish([]string{"channel", "event"}, "key1", "data1")
        assert.Nil(t, err)
        m.AssertExpectations(t)
 }
+
+func TestWriteAndPublishSeveralChannelsAndEvents(t *testing.T) {
+       m, i := setup()
+
+       expectedChannelsAndEvents := []string{"{namespace},channel1", "event1___event2",
+               "{namespace},channel2", "event3___event4"}
+       expectedKeyVal := []interface{}{"{namespace},key", "data"}
+
+       verifyChannelAndEvent := func(input []string) bool {
+               return verifySliceInOrder(input, expectedChannelsAndEvents)
+       }
+       m.On("MSetMPub", mock.MatchedBy(verifyChannelAndEvent), expectedKeyVal).Return(nil)
+       m.AssertNotCalled(t, "MSet", expectedKeyVal)
+       err := i.SetAndPublish([]string{"channel1", "event1", "channel2", "event3", "channel1", "event2", "channel2", "event4"},
+               "key", "data")
+       assert.Nil(t, err)
+       m.AssertExpectations(t)
+}
+
 func TestWriteAndPublishOneKeyOneChannelTwoEvents(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedMessage := "event1___event2"
+       expectedChannelAndEvents := []string{"{namespace},channel", "event1___event2"}
        expectedKeyVal := []interface{}{"{namespace},key1", "data1"}
 
-       m.On("MSetPub", expectedChannel, expectedMessage, expectedKeyVal).Return(nil)
+       m.On("MSetMPub", expectedChannelAndEvents, expectedKeyVal).Return(nil)
        m.AssertNotCalled(t, "MSet", expectedKeyVal)
        err := i.SetAndPublish([]string{"channel", "event1", "channel", "event2"}, "key1", "data1")
        assert.Nil(t, err)
@@ -399,10 +534,9 @@ func TestWriteAndPublishOneKeyOneChannelTwoEvents(t *testing.T) {
 func TestWriteAndPublishIncorrectChannelAndEvent(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedMessage := "event1___event2"
+       expectedChannelAndEvent := []string{}
        expectedKeyVal := []interface{}{"{namespace},key1", "data1"}
-       m.AssertNotCalled(t, "MSetPub", expectedChannel, expectedMessage, expectedKeyVal)
+       m.AssertNotCalled(t, "MSetMPub", expectedChannelAndEvent, expectedKeyVal)
        m.AssertNotCalled(t, "MSet", expectedKeyVal)
        err := i.SetAndPublish([]string{"channel", "event1", "channel"}, "key1", "data1")
        assert.NotNil(t, err)
@@ -412,10 +546,9 @@ func TestWriteAndPublishIncorrectChannelAndEvent(t *testing.T) {
 func TestWriteAndPublishNotAllowedCharactersInEvents(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedMessage := "event1___event2"
+       expectedChannelAndEvent := []string{}
        expectedKeyVal := []interface{}{"{namespace},key1", "data1"}
-       m.AssertNotCalled(t, "MSetPub", expectedChannel, expectedMessage, expectedKeyVal)
+       m.AssertNotCalled(t, "MSetMPub", expectedChannelAndEvent, expectedKeyVal)
        m.AssertNotCalled(t, "MSet", expectedKeyVal)
        err := i.SetAndPublish([]string{"channel", "event1___event2"}, "key1", "data1")
        assert.NotNil(t, err)
@@ -425,11 +558,10 @@ func TestWriteAndPublishNotAllowedCharactersInEvents(t *testing.T) {
 func TestWriteAndPublishNoData(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedMessage := "event"
+       expectedChannelAndEvent := []string{}
        expectedKeyVal := []interface{}{"key"}
 
-       m.AssertNotCalled(t, "MSetPub", expectedChannel, expectedMessage, expectedKeyVal)
+       m.AssertNotCalled(t, "MSetMPub", expectedChannelAndEvent, expectedKeyVal)
        m.AssertNotCalled(t, "MSet", expectedKeyVal)
        err := i.SetAndPublish([]string{"channel", "event"}, []interface{}{"key"})
        assert.NotNil(t, err)
@@ -442,7 +574,7 @@ func TestWriteAndPublishNoChannelEvent(t *testing.T) {
        expectedKeyVal := []interface{}{"{namespace},key1", "data1"}
 
        m.On("MSet", expectedKeyVal).Return(nil)
-       m.AssertNotCalled(t, "MSetPub", "", "", expectedKeyVal)
+       m.AssertNotCalled(t, "MSetMPub", "", "", expectedKeyVal)
        err := i.SetAndPublish([]string{}, "key1", "data1")
        assert.Nil(t, err)
        m.AssertExpectations(t)
@@ -452,23 +584,39 @@ func TestWriteAndPublishNoChannelEvent(t *testing.T) {
 func TestRemoveAndPublishSuccessfully(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        expectedKeys := []string{"{namespace},key1", "{namespace},key2"}
 
-       m.On("DelPub", expectedChannel, expectedEvent, expectedKeys).Return(nil)
+       m.On("DelMPub", expectedChannelAndEvent, expectedKeys).Return(nil)
        err := i.RemoveAndPublish([]string{"channel", "event"}, []string{"key1", "key2"})
        assert.Nil(t, err)
        m.AssertExpectations(t)
 }
+
+func TestRemoveAndPublishSeveralChannelsAndEventsSuccessfully(t *testing.T) {
+       m, i := setup()
+
+       expectedChannelAndEvent := []string{"{namespace},channel1", "event1___event2",
+               "{namespace},channel2", "event3___event4"}
+       expectedKeys := []string{"{namespace},key1", "{namespace},key2"}
+
+       verifyChannelAndEvent := func(input []string) bool {
+               return verifySliceInOrder(input, expectedChannelAndEvent)
+       }
+       m.On("DelMPub", mock.MatchedBy(verifyChannelAndEvent), expectedKeys).Return(nil)
+       err := i.RemoveAndPublish([]string{"channel1", "event1", "channel2", "event3",
+               "channel1", "event2", "channel2", "event4"},
+               []string{"key1", "key2"})
+       assert.Nil(t, err)
+       m.AssertExpectations(t)
+}
 func TestRemoveAndPublishFail(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        expectedKeys := []string{"{namespace},key1", "{namespace},key2"}
 
-       m.On("DelPub", expectedChannel, expectedEvent, expectedKeys).Return(errors.New("Some error"))
+       m.On("DelMPub", expectedChannelAndEvent, expectedKeys).Return(errors.New("Some error"))
        err := i.RemoveAndPublish([]string{"channel", "event"}, []string{"key1", "key2"})
        assert.NotNil(t, err)
        m.AssertExpectations(t)
@@ -488,25 +636,23 @@ func TestRemoveAndPublishNoChannels(t *testing.T) {
 func TestRemoveAndPublishIncorrectChannel(t *testing.T) {
        m, i := setup()
 
-       notExpectedChannel := "{namespace},channel"
-       notExpectedEvent := "event"
+       notExpectedChannelAndEvent := []string{}
        notExpectedKeys := []string{"{namespace},key"}
 
-       m.AssertNotCalled(t, "DelPub", notExpectedChannel, notExpectedEvent, notExpectedKeys)
+       m.AssertNotCalled(t, "DelMPub", notExpectedChannelAndEvent, notExpectedKeys)
        m.AssertNotCalled(t, "Del", notExpectedKeys)
-       err := i.RemoveAndPublish([]string{"channel", "event", "channel2"}, []string{})
-       assert.Nil(t, err)
+       err := i.RemoveAndPublish([]string{"channel", "event", "channel2"}, []string{"key1", "key2"})
+       assert.NotNil(t, err)
        m.AssertExpectations(t)
 
 }
 func TestRemoveAndPublishNoKeys(t *testing.T) {
        m, i := setup()
 
-       notExpectedChannel := "{namespace},channel"
-       notExpectedEvent := "event"
+       notExpectedChannelAndEvent := []string{}
        notExpectedKeys := []string{"{namespace},key"}
 
-       m.AssertNotCalled(t, "DelPub", notExpectedChannel, notExpectedEvent, notExpectedKeys)
+       m.AssertNotCalled(t, "DelMPub", notExpectedChannelAndEvent, notExpectedKeys)
        m.AssertNotCalled(t, "Del", notExpectedKeys)
        err := i.RemoveAndPublish([]string{"channel", "event"}, []string{})
        assert.Nil(t, err)
@@ -684,12 +830,11 @@ func TestSetIfFailure(t *testing.T) {
 func TestSetIfAndPublishSuccessfully(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        expectedKey := "{namespace},key"
        expectedOldData := interface{}("olddata")
        expectedNewData := interface{}("newdata")
-       m.On("SetIEPub", expectedChannel, expectedEvent, expectedKey, expectedOldData, expectedNewData).Return(true, nil)
+       m.On("SetIEPub", expectedChannelAndEvent, expectedKey, expectedOldData, expectedNewData).Return(true, nil)
        status, err := i.SetIfAndPublish([]string{"channel", "event"}, "key", "olddata", "newdata")
        assert.Nil(t, err)
        assert.True(t, status)
@@ -699,12 +844,11 @@ func TestSetIfAndPublishSuccessfully(t *testing.T) {
 func TestSetIfAndPublishIncorrectChannelAndEvent(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        expectedKey := "{namespace},key"
        expectedOldData := interface{}("olddata")
        expectedNewData := interface{}("newdata")
-       m.AssertNotCalled(t, "SetIEPub", expectedChannel, expectedEvent, expectedKey, expectedOldData, expectedNewData)
+       m.AssertNotCalled(t, "SetIEPub", expectedChannelAndEvent, expectedKey, expectedOldData, expectedNewData)
        m.AssertNotCalled(t, "SetIE", expectedKey, expectedOldData, expectedNewData)
        status, err := i.SetIfAndPublish([]string{"channel", "event1", "channel"}, "key", "olddata", "newdata")
        assert.NotNil(t, err)
@@ -714,12 +858,11 @@ func TestSetIfAndPublishIncorrectChannelAndEvent(t *testing.T) {
 func TestSetIfAndPublishNOKStatus(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        expectedKey := "{namespace},key"
        expectedOldData := interface{}("olddata")
        expectedNewData := interface{}("newdata")
-       m.On("SetIEPub", expectedChannel, expectedEvent, expectedKey, expectedOldData, expectedNewData).Return(false, nil)
+       m.On("SetIEPub", expectedChannelAndEvent, expectedKey, expectedOldData, expectedNewData).Return(false, nil)
        status, err := i.SetIfAndPublish([]string{"channel", "event"}, "key", "olddata", "newdata")
        assert.Nil(t, err)
        assert.False(t, status)
@@ -742,12 +885,11 @@ func TestSetIfAndPublishNoChannels(t *testing.T) {
 func TestSetIfNotExistsAndPublishSuccessfully(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        expectedKey := "{namespace},key"
        expectedData := interface{}("data")
 
-       m.On("SetNXPub", expectedChannel, expectedEvent, expectedKey, expectedData).Return(true, nil)
+       m.On("SetNXPub", expectedChannelAndEvent, expectedKey, expectedData).Return(true, nil)
        status, err := i.SetIfNotExistsAndPublish([]string{"channel", "event"}, "key", "data")
        assert.Nil(t, err)
        assert.True(t, status)
@@ -757,12 +899,11 @@ func TestSetIfNotExistsAndPublishSuccessfully(t *testing.T) {
 func TestSetIfNotExistsAndPublishSeveralEvents(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event1___event2"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event1___event2"}
        expectedKey := "{namespace},key"
        expectedData := interface{}("data")
 
-       m.On("SetNXPub", expectedChannel, expectedEvent, expectedKey, expectedData).Return(true, nil)
+       m.On("SetNXPub", expectedChannelAndEvent, expectedKey, expectedData).Return(true, nil)
        status, err := i.SetIfNotExistsAndPublish([]string{"channel", "event1", "channel", "event2"}, "key", "data")
        assert.Nil(t, err)
        assert.True(t, status)
@@ -775,7 +916,7 @@ func TestSetIfNotExistsAndPublishNoChannels(t *testing.T) {
        expectedKey := "{namespace},key"
        expectedData := interface{}("data")
 
-       m.On("SetNX", expectedKey, expectedData).Return(true, nil)
+       m.On("SetNX", expectedKey, expectedData, time.Duration(0)).Return(true, nil)
        status, err := i.SetIfNotExistsAndPublish([]string{}, "key", "data")
        assert.Nil(t, err)
        assert.True(t, status)
@@ -785,12 +926,11 @@ func TestSetIfNotExistsAndPublishNoChannels(t *testing.T) {
 func TestSetIfNotExistsAndPublishFail(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        expectedKey := "{namespace},key"
        expectedData := interface{}("data")
 
-       m.On("SetNXPub", expectedChannel, expectedEvent, expectedKey, expectedData).Return(false, nil)
+       m.On("SetNXPub", expectedChannelAndEvent, expectedKey, expectedData).Return(false, nil)
        status, err := i.SetIfNotExistsAndPublish([]string{"channel", "event"}, "key", "data")
        assert.Nil(t, err)
        assert.False(t, status)
@@ -800,13 +940,12 @@ func TestSetIfNotExistsAndPublishFail(t *testing.T) {
 func TestSetIfNotExistsAndPublishIncorrectChannels(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        expectedKey := "{namespace},key"
        expectedData := interface{}("data")
 
-       m.AssertNotCalled(t, "SetNXPub", expectedChannel, expectedEvent, expectedKey, expectedData)
-       m.AssertNotCalled(t, "SetNX", expectedKey, expectedData)
+       m.AssertNotCalled(t, "SetNXPub", expectedChannelAndEvent, expectedKey, expectedData)
+       m.AssertNotCalled(t, "SetNX", expectedKey, expectedData, 0)
        status, err := i.SetIfNotExistsAndPublish([]string{"channel", "event", "channel2"}, "key", "data")
        assert.NotNil(t, err)
        assert.False(t, status)
@@ -816,12 +955,11 @@ func TestSetIfNotExistsAndPublishIncorrectChannels(t *testing.T) {
 func TestSetIfNotExistsAndPublishError(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        expectedKey := "{namespace},key"
        expectedData := interface{}("data")
 
-       m.On("SetNXPub", expectedChannel, expectedEvent, expectedKey, expectedData).Return(false, errors.New("Some error"))
+       m.On("SetNXPub", expectedChannelAndEvent, expectedKey, expectedData).Return(false, errors.New("Some error"))
        status, err := i.SetIfNotExistsAndPublish([]string{"channel", "event"}, "key", "data")
        assert.NotNil(t, err)
        assert.False(t, status)
@@ -833,7 +971,7 @@ func TestSetIfNotExistsSuccessfullyOkStatus(t *testing.T) {
 
        mSetNXExpectedKey := string("{namespace},key1")
        mSetNXExpectedData := interface{}("data")
-       m.On("SetNX", mSetNXExpectedKey, mSetNXExpectedData).Return(true, nil)
+       m.On("SetNX", mSetNXExpectedKey, mSetNXExpectedData, time.Duration(0)).Return(true, nil)
        status, err := i.SetIfNotExists("key1", "data")
        assert.Nil(t, err)
        assert.True(t, status)
@@ -845,7 +983,7 @@ func TestSetIfNotExistsSuccessfullyNOKStatus(t *testing.T) {
 
        mSetNXExpectedKey := string("{namespace},key1")
        mSetNXExpectedData := interface{}("data")
-       m.On("SetNX", mSetNXExpectedKey, mSetNXExpectedData).Return(false, nil)
+       m.On("SetNX", mSetNXExpectedKey, mSetNXExpectedData, time.Duration(0)).Return(false, nil)
        status, err := i.SetIfNotExists("key1", "data")
        assert.Nil(t, err)
        assert.False(t, status)
@@ -857,7 +995,7 @@ func TestSetIfNotExistsFailure(t *testing.T) {
 
        mSetNXExpectedKey := string("{namespace},key1")
        mSetNXExpectedData := interface{}("data")
-       m.On("SetNX", mSetNXExpectedKey, mSetNXExpectedData).Return(false, errors.New("Some error"))
+       m.On("SetNX", mSetNXExpectedKey, mSetNXExpectedData, time.Duration(0)).Return(false, errors.New("Some error"))
        status, err := i.SetIfNotExists("key1", "data")
        assert.NotNil(t, err)
        assert.False(t, status)
@@ -867,12 +1005,11 @@ func TestSetIfNotExistsFailure(t *testing.T) {
 func TestRemoveIfAndPublishSuccessfully(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event1___event2"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event1___event2"}
        expectedKey := "{namespace},key"
        expectedValue := interface{}("data")
 
-       m.On("DelIEPub", expectedChannel, expectedEvent, expectedKey, expectedValue).Return(true, nil)
+       m.On("DelIEPub", expectedChannelAndEvent, expectedKey, expectedValue).Return(true, nil)
        status, err := i.RemoveIfAndPublish([]string{"channel", "event1", "channel", "event2"}, "key", "data")
        assert.Nil(t, err)
        assert.True(t, status)
@@ -882,12 +1019,11 @@ func TestRemoveIfAndPublishSuccessfully(t *testing.T) {
 func TestRemoveIfAndPublishNok(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event1___event2"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event1___event2"}
        expectedKey := "{namespace},key"
        expectedValue := interface{}("data")
 
-       m.On("DelIEPub", expectedChannel, expectedEvent, expectedKey, expectedValue).Return(false, nil)
+       m.On("DelIEPub", expectedChannelAndEvent, expectedKey, expectedValue).Return(false, nil)
        status, err := i.RemoveIfAndPublish([]string{"channel", "event1", "channel", "event2"}, "key", "data")
        assert.Nil(t, err)
        assert.False(t, status)
@@ -897,12 +1033,11 @@ func TestRemoveIfAndPublishNok(t *testing.T) {
 func TestRemoveIfAndPublishError(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event1___event2"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event1___event2"}
        expectedKey := "{namespace},key"
        expectedValue := interface{}("data")
 
-       m.On("DelIEPub", expectedChannel, expectedEvent, expectedKey, expectedValue).Return(false, errors.New("Some error"))
+       m.On("DelIEPub", expectedChannelAndEvent, expectedKey, expectedValue).Return(false, errors.New("Some error"))
        status, err := i.RemoveIfAndPublish([]string{"channel", "event1", "channel", "event2"}, "key", "data")
        assert.NotNil(t, err)
        assert.False(t, status)
@@ -912,12 +1047,11 @@ func TestRemoveIfAndPublishError(t *testing.T) {
 func TestRemoveIfAndPublishIncorrectChannel(t *testing.T) {
        m, i := setup()
 
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        expectedKey := "{namespace},key"
        expectedValue := interface{}("data")
 
-       m.AssertNotCalled(t, "DelIEPub", expectedChannel, expectedEvent, expectedKey, expectedValue)
+       m.AssertNotCalled(t, "DelIEPub", expectedChannelAndEvent, expectedKey, expectedValue)
        m.AssertNotCalled(t, "DelIE", expectedKey, expectedValue)
        status, err := i.RemoveIfAndPublish([]string{"channel", "event1", "channel"}, "key", "data")
        assert.NotNil(t, err)
@@ -980,10 +1114,9 @@ func TestRemoveAllAndPublishSuccessfully(t *testing.T) {
        mKeysExpected := string("{namespace},*")
        mKeysReturn := []string{"{namespace},key1", "{namespace},key2"}
        mDelExpected := mKeysReturn
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        m.On("Keys", mKeysExpected).Return(mKeysReturn, nil)
-       m.On("DelPub", expectedChannel, expectedEvent, mDelExpected).Return(nil)
+       m.On("DelMPub", expectedChannelAndEvent, mDelExpected).Return(nil)
        err := i.RemoveAllAndPublish([]string{"channel", "event"})
        assert.Nil(t, err)
        m.AssertExpectations(t)
@@ -995,10 +1128,9 @@ func TestRemoveAllAndPublishKeysReturnError(t *testing.T) {
        mKeysExpected := string("{namespace},*")
        mKeysReturn := []string{"{namespace},key1", "{namespace},key2"}
        mDelExpected := mKeysReturn
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        m.On("Keys", mKeysExpected).Return(mKeysReturn, errors.New("Some error"))
-       m.AssertNotCalled(t, "DelPub", expectedChannel, expectedEvent, mDelExpected)
+       m.AssertNotCalled(t, "DelMPub", expectedChannelAndEvent, mDelExpected)
        err := i.RemoveAllAndPublish([]string{"channel", "event"})
        assert.NotNil(t, err)
        m.AssertExpectations(t)
@@ -1010,10 +1142,9 @@ func TestRemoveAllAndPublishKeysDelReturnsError(t *testing.T) {
        mKeysExpected := string("{namespace},*")
        mKeysReturn := []string{"{namespace},key1", "{namespace},key2"}
        mDelExpected := mKeysReturn
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        m.On("Keys", mKeysExpected).Return(mKeysReturn, nil)
-       m.On("DelPub", expectedChannel, expectedEvent, mDelExpected).Return(errors.New("Some error"))
+       m.On("DelMPub", expectedChannelAndEvent, mDelExpected).Return(errors.New("Some error"))
        err := i.RemoveAllAndPublish([]string{"channel", "event"})
        assert.NotNil(t, err)
        m.AssertExpectations(t)
@@ -1025,10 +1156,9 @@ func TestRemoveAllAndPublishKeysEventsWithIllegalCharacters(t *testing.T) {
        mKeysExpected := string("{namespace},*")
        mKeysReturn := []string{"{namespace},key1", "{namespace},key2"}
        mDelExpected := mKeysReturn
-       expectedChannel := "{namespace},channel"
-       expectedEvent := "event"
+       expectedChannelAndEvent := []string{"{namespace},channel", "event"}
        m.On("Keys", mKeysExpected).Return(mKeysReturn, nil)
-       m.AssertNotCalled(t, "DelPub", expectedChannel, expectedEvent, mDelExpected)
+       m.AssertNotCalled(t, "DelMPub", expectedChannelAndEvent, mDelExpected)
        err := i.RemoveAllAndPublish([]string{"channel", "event___anotherEvent"})
        assert.NotNil(t, err)
        m.AssertExpectations(t)
@@ -1043,7 +1173,7 @@ func TestRemoveAllAndPublishNoChannels(t *testing.T) {
        mDelExpected := mKeysReturn
        m.On("Keys", mKeysExpected).Return(mKeysReturn, nil)
        m.On("Del", mDelExpected).Return(nil)
-       m.AssertNotCalled(t, "DelPub", "", "", mDelExpected)
+       m.AssertNotCalled(t, "DelMPub", "", "", mDelExpected)
        err := i.RemoveAllAndPublish([]string{})
        assert.Nil(t, err)
        m.AssertExpectations(t)
@@ -1056,7 +1186,7 @@ func TestRemoveAllAndPublishIncorrectChannel(t *testing.T) {
        mKeysReturn := []string{"{namespace},key1", "{namespace},key2"}
        mDelExpected := mKeysReturn
        m.On("Keys", mKeysExpected).Return(mKeysReturn, nil)
-       m.AssertNotCalled(t, "DelPub", "", "", mDelExpected)
+       m.AssertNotCalled(t, "DelMPub", "", "", mDelExpected)
        err := i.RemoveAllAndPublish([]string{"channel", "event", "channel2"})
        assert.NotNil(t, err)
        m.AssertExpectations(t)
@@ -1146,7 +1276,7 @@ func TestGetMembersSuccessfully(t *testing.T) {
        m.On("SMembers", groupExpected).Return(returnExpected, nil)
 
        result, err := i.GetMembers("group")
-       assert.Nil(t,err)
+       assert.Nil(t, err)
        assert.Equal(t, result, returnExpected)
        m.AssertExpectations(t)
 }
@@ -1159,7 +1289,7 @@ func TestGetMembersFail(t *testing.T) {
        m.On("SMembers", groupExpected).Return(returnExpected, errors.New("Some error"))
 
        result, err := i.GetMembers("group")
-       assert.NotNil(t,err)
+       assert.NotNil(t, err)
        assert.Equal(t, []string{}, result)
        m.AssertExpectations(t)
 }
@@ -1231,4 +1361,195 @@ func TestGroupSizeFail(t *testing.T) {
        assert.NotNil(t, err)
        assert.Equal(t, int64(0), result)
        m.AssertExpectations(t)
-}
\ No newline at end of file
+}
+
+func TestLockResourceSuccessfully(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(true, nil)
+
+       lock, err := i.LockResource("resource", time.Duration(1), &sdlgo.Options{})
+       assert.Nil(t, err)
+       assert.NotNil(t, lock)
+       m.AssertExpectations(t)
+}
+
+func TestLockResourceFailure(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(true, errors.New("Some error"))
+
+       lock, err := i.LockResource("resource", time.Duration(1), &sdlgo.Options{})
+       assert.NotNil(t, err)
+       assert.Nil(t, lock)
+       m.AssertExpectations(t)
+}
+
+func TestLockResourceTrySeveralTimesSuccessfully(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(false, nil).Once()
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(true, nil).Once()
+
+       lock, err := i.LockResource("resource", time.Duration(1), &sdlgo.Options{
+               RetryCount: 2,
+       })
+       assert.Nil(t, err)
+       assert.NotNil(t, lock)
+       m.AssertExpectations(t)
+}
+
+func TestLockResourceTrySeveralTimesFailure(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(false, nil).Once()
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(true, errors.New("Some error")).Once()
+
+       lock, err := i.LockResource("resource", time.Duration(1), &sdlgo.Options{
+               RetryCount: 2,
+       })
+       assert.NotNil(t, err)
+       assert.Nil(t, lock)
+       m.AssertExpectations(t)
+}
+
+func TestLockResourceTrySeveralTimesUnableToGetResource(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(false, nil).Once()
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(false, nil).Once()
+
+       lock, err := i.LockResource("resource", time.Duration(1), &sdlgo.Options{
+               RetryCount: 1,
+       })
+       assert.NotNil(t, err)
+       assert.EqualError(t, err, "Lock not obtained")
+       assert.Nil(t, lock)
+       m.AssertExpectations(t)
+}
+
+func TestReleaseResourceSuccessfully(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(true, nil).Once()
+       m.On("DelIE", resourceExpected, mock.Anything).Return(true, nil).Once()
+
+       lock, err := i.LockResource("resource", time.Duration(1), &sdlgo.Options{
+               RetryCount: 1,
+       })
+       err2 := lock.ReleaseResource()
+       assert.Nil(t, err)
+       assert.NotNil(t, lock)
+       assert.Nil(t, err2)
+       m.AssertExpectations(t)
+}
+
+func TestReleaseResourceFailure(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(true, nil).Once()
+       m.On("DelIE", resourceExpected, mock.Anything).Return(true, errors.New("Some error")).Once()
+
+       lock, err := i.LockResource("resource", time.Duration(1), &sdlgo.Options{
+               RetryCount: 1,
+       })
+       err2 := lock.ReleaseResource()
+       assert.Nil(t, err)
+       assert.NotNil(t, lock)
+       assert.NotNil(t, err2)
+       m.AssertExpectations(t)
+}
+
+func TestReleaseResourceLockNotHeld(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(true, nil).Once()
+       m.On("DelIE", resourceExpected, mock.Anything).Return(false, nil).Once()
+
+       lock, err := i.LockResource("resource", time.Duration(1), &sdlgo.Options{
+               RetryCount: 1,
+       })
+       err2 := lock.ReleaseResource()
+       assert.Nil(t, err)
+       assert.NotNil(t, lock)
+       assert.NotNil(t, err2)
+       assert.EqualError(t, err2, "Lock not held")
+       m.AssertExpectations(t)
+}
+
+func TestRefreshResourceSuccessfully(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(true, nil).Once()
+       m.On("PExpireIE", resourceExpected, mock.Anything, time.Duration(1)).Return(nil).Once()
+
+       lock, err := i.LockResource("resource", time.Duration(1), &sdlgo.Options{
+               RetryCount: 1,
+       })
+       err2 := lock.RefreshResource(time.Duration(1))
+       assert.Nil(t, err)
+       assert.NotNil(t, lock)
+       assert.Nil(t, err2)
+       m.AssertExpectations(t)
+}
+
+func TestRefreshResourceFailure(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("SetNX", resourceExpected, mock.Anything, time.Duration(1)).Return(true, nil).Once()
+       m.On("PExpireIE", resourceExpected, mock.Anything, time.Duration(1)).Return(errors.New("Some error")).Once()
+
+       lock, err := i.LockResource("resource", time.Duration(1), &sdlgo.Options{
+               RetryCount: 1,
+       })
+       err2 := lock.RefreshResource(time.Duration(1))
+       assert.Nil(t, err)
+       assert.NotNil(t, lock)
+       assert.NotNil(t, err2)
+       m.AssertExpectations(t)
+}
+
+func TestCheckResourceSuccessfully(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("PTTL", resourceExpected).Return(time.Duration(1), nil)
+       result, err := i.CheckResource("resource")
+       assert.Nil(t, err)
+       assert.Equal(t, result, time.Duration(1))
+       m.AssertExpectations(t)
+}
+
+func TestCheckResourceFailure(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("PTTL", resourceExpected).Return(time.Duration(1), errors.New("Some error"))
+       result, err := i.CheckResource("resource")
+       assert.NotNil(t, err)
+       assert.EqualError(t, err, "Some error")
+       assert.Equal(t, result, time.Duration(0))
+       m.AssertExpectations(t)
+}
+
+func TestCheckResourceInvalidResource(t *testing.T) {
+       m, i := setup()
+
+       resourceExpected := "{namespace},resource"
+       m.On("PTTL", resourceExpected).Return(time.Duration(-1), nil)
+       result, err := i.CheckResource("resource")
+       assert.NotNil(t, err)
+       assert.EqualError(t, err, "invalid resource given, no expiration time attached")
+       assert.Equal(t, result, time.Duration(0))
+       m.AssertExpectations(t)
+}