blob: 8eae0151299fbf1f29b902ab3dc616162ee1a276 [file] [log] [blame]
Girish Kumarca522102019-11-08 11:26:35 +00001/*
2 * Copyright 2019-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package db
18
19import (
20 "context"
serkant.uluderyab38671c2019-11-01 09:35:38 -070021 "os"
22 "testing"
23 "time"
24
25 "github.com/opencord/voltha-lib-go/v3/pkg/mocks"
khenaidooc7005fc2019-11-18 19:23:57 -050026 "github.com/phayes/freeport"
Girish Kumarca522102019-11-08 11:26:35 +000027 "github.com/stretchr/testify/assert"
28 "google.golang.org/grpc/codes"
29 "google.golang.org/grpc/status"
Girish Kumarca522102019-11-08 11:26:35 +000030)
31
Girish Kumarca522102019-11-08 11:26:35 +000032const (
33 embedEtcdServerHost = "localhost"
Girish Kumarca522102019-11-08 11:26:35 +000034 defaultTimeout = 1
35 defaultPathPrefix = "Prefix"
36)
37
khenaidooc7005fc2019-11-18 19:23:57 -050038var (
39 embedEtcdServerPort int
40 dummyEtcdServerPort int
41)
Girish Kumarca522102019-11-08 11:26:35 +000042
khenaidooc7005fc2019-11-18 19:23:57 -050043func TestMain(m *testing.M) {
44 var err error
45 embedEtcdServerPort, err = freeport.GetFreePort()
46 if err != nil {
khenaidoob332f9b2020-01-16 16:25:26 -050047 logger.Fatal(err)
khenaidooc7005fc2019-11-18 19:23:57 -050048 }
49 dummyEtcdServerPort, err = freeport.GetFreePort()
50 if err != nil {
khenaidoob332f9b2020-01-16 16:25:26 -050051 logger.Fatal(err)
khenaidooc7005fc2019-11-18 19:23:57 -050052 }
53 peerPort, err := freeport.GetFreePort()
54 if err != nil {
khenaidoob332f9b2020-01-16 16:25:26 -050055 logger.Fatal(err)
khenaidooc7005fc2019-11-18 19:23:57 -050056 }
57 etcdServer := mocks.StartEtcdServer(mocks.MKConfig("voltha.db.test", embedEtcdServerPort, peerPort, "voltha.lib.db", "error"))
Girish Kumarca522102019-11-08 11:26:35 +000058 res := m.Run()
59
60 etcdServer.Stop()
61 os.Exit(res)
62}
63
64func provisionBackendWithEmbeddedEtcdServer(t *testing.T) *Backend {
65 backend := NewBackend("etcd", embedEtcdServerHost, embedEtcdServerPort, defaultTimeout, defaultPathPrefix)
66 assert.NotNil(t, backend)
67 assert.NotNil(t, backend.Client)
68 return backend
69}
70
71func provisionBackendWithDummyEtcdServer(t *testing.T) *Backend {
72 backend := NewBackend("etcd", embedEtcdServerHost, dummyEtcdServerPort, defaultTimeout, defaultPathPrefix)
73 assert.NotNil(t, backend)
74 assert.NotNil(t, backend.Client)
75 return backend
76}
77
78// Create instance using Etcd Kvstore
79func TestNewBackend_EtcdKvStore(t *testing.T) {
80 backend := NewBackend("etcd", embedEtcdServerHost, embedEtcdServerPort, defaultTimeout, defaultPathPrefix)
81
82 // Verify all attributes of backend have got set correctly
83 assert.NotNil(t, backend)
84 assert.NotNil(t, backend.Client)
85 assert.Equal(t, backend.StoreType, "etcd")
86 assert.Equal(t, backend.Host, embedEtcdServerHost)
87 assert.Equal(t, backend.Port, embedEtcdServerPort)
88 assert.Equal(t, backend.Timeout, defaultTimeout)
89 assert.Equal(t, backend.PathPrefix, defaultPathPrefix)
90 assert.Equal(t, backend.alive, false) // backend is not alive at start
91 assert.Nil(t, backend.liveness) // no liveness channel is created at start
92 assert.Equal(t, backend.LivenessChannelInterval, DefaultLivenessChannelInterval)
93}
94
95// Create instance using Consul Kvstore
96func TestNewBackend_ConsulKvStore(t *testing.T) {
97 backend := NewBackend("consul", embedEtcdServerHost, embedEtcdServerPort, defaultTimeout, defaultPathPrefix)
98
99 // Verify kvstore type attribute of backend has got set correctly
100 assert.NotNil(t, backend)
101 assert.NotNil(t, backend.Client)
102 assert.Equal(t, backend.StoreType, "consul")
103}
104
105// Create instance using Invalid Kvstore; instance creation should fail
106func TestNewBackend_InvalidKvstore(t *testing.T) {
107 backend := NewBackend("unknown", embedEtcdServerHost, embedEtcdServerPort, defaultTimeout, defaultPathPrefix)
108
109 assert.NotNil(t, backend)
110 assert.Nil(t, backend.Client)
111}
112
113func TestMakePath(t *testing.T) {
114 backend := provisionBackendWithEmbeddedEtcdServer(t)
115 path := backend.makePath("Suffix")
116 assert.Equal(t, defaultPathPrefix+"/Suffix", path)
117}
118
119// Liveness Check against Embedded Etcd Server should return alive state
120func TestPerformLivenessCheck_EmbeddedEtcdServer(t *testing.T) {
121 backend := provisionBackendWithEmbeddedEtcdServer(t)
122 alive := backend.PerformLivenessCheck(defaultTimeout)
123 assert.True(t, alive)
124}
125
126// Liveness Check against Dummy Etcd Server should return not-alive state
127func TestPerformLivenessCheck_DummyEtcdServer(t *testing.T) {
128 backend := provisionBackendWithDummyEtcdServer(t)
129 alive := backend.PerformLivenessCheck(defaultTimeout)
130 assert.False(t, alive)
131}
132
133// Enabling Liveness Channel before First Liveness Check
134func TestEnableLivenessChannel_EmbeddedEtcdServer_BeforeLivenessCheck(t *testing.T) {
135 backend := provisionBackendWithEmbeddedEtcdServer(t)
136
137 alive := backend.EnableLivenessChannel()
138 assert.NotNil(t, alive)
139 assert.Equal(t, 1, len(alive))
140 assert.Equal(t, false, <-alive)
141 assert.NotNil(t, backend.liveness)
142}
143
144// Enabling Liveness Channel after First Liveness Check
145func TestEnableLivenessChannel_EmbeddedEtcdServer_AfterLivenessCheck(t *testing.T) {
146 backend := provisionBackendWithEmbeddedEtcdServer(t)
147 backend.PerformLivenessCheck(defaultTimeout)
148
149 alive := backend.EnableLivenessChannel()
150 assert.NotNil(t, alive)
151 assert.Equal(t, 1, len(alive))
152 assert.Equal(t, true, <-alive)
153 assert.NotNil(t, backend.liveness)
154}
155
156// Update Liveness with alive status change
157func TestUpdateLiveness_AliveStatusChange(t *testing.T) {
158 backend := provisionBackendWithEmbeddedEtcdServer(t)
159 // Enable Liveness Channel and verify initial state is not-alive
160 aliveState := backend.EnableLivenessChannel()
161 assert.NotNil(t, aliveState)
162 assert.Equal(t, 1, len(backend.liveness))
163 assert.Equal(t, false, <-backend.liveness)
164 lastUpdateTime := backend.lastLivenessTime
165
166 // Update with changed alive state. Verify alive state push & liveness time update
167 backend.updateLiveness(true)
168 assert.Equal(t, 1, len(backend.liveness))
169 assert.Equal(t, true, <-backend.liveness)
170 assert.NotEqual(t, lastUpdateTime, backend.lastLivenessTime)
171}
172
173// Update Liveness with same alive status reporting
174func TestUpdateLiveness_AliveStatusUnchanged(t *testing.T) {
175 backend := provisionBackendWithEmbeddedEtcdServer(t)
176 // Enable Liveness Channel and verify initial state is not-alive
177 aliveState := backend.EnableLivenessChannel()
178 assert.NotNil(t, aliveState)
179 assert.Equal(t, false, <-backend.liveness)
180 lastUpdateTime := backend.lastLivenessTime
181
182 // Update with same alive state. Verify no further alive state push
183 backend.updateLiveness(false)
184 assert.Equal(t, 0, len(backend.liveness))
185 assert.Equal(t, lastUpdateTime, backend.lastLivenessTime)
186
187 // Now set lastUpdateTime 10 min back and push again
188 tenMinDuration, _ := time.ParseDuration("10m")
189 backend.lastLivenessTime = time.Now().Add(-tenMinDuration)
190 lastUpdateTime = backend.lastLivenessTime
191
192 backend.updateLiveness(false)
193 assert.Equal(t, 1, len(backend.liveness))
194 assert.Equal(t, false, <-backend.liveness)
195 assert.NotEqual(t, lastUpdateTime, backend.lastLivenessTime)
196}
197
198func TestIsErrorIndicatingAliveKvstore(t *testing.T) {
199 tests := []struct {
200 name string
201 arg error
202 want bool
203 }{
204 {"No Error", nil, true},
205 {"Request Canceled", context.Canceled, true},
206 {"Request Timeout", context.DeadlineExceeded, false},
207 {"Etcd Error - InvalidArgument", status.New(codes.InvalidArgument, "").Err(), true},
208 {"Etcd Error - DeadlineExceeded", status.New(codes.DeadlineExceeded, "").Err(), false},
209 {"Etcd Error - Unavailable", status.New(codes.Unavailable, "").Err(), false},
210 {"Etcd Error - DataLoss", status.New(codes.DataLoss, "").Err(), false},
211 {"Etcd Error - NotFound", status.New(codes.NotFound, "").Err(), true},
212 {"Etcd Error - PermissionDenied ", status.New(codes.PermissionDenied, "").Err(), true},
213 {"Etcd Error - FailedPrecondition ", status.New(codes.FailedPrecondition, "").Err(), true},
214 }
215
216 backend := provisionBackendWithEmbeddedEtcdServer(t)
217
218 for _, tt := range tests {
219 t.Run(tt.name, func(t *testing.T) {
220 if backend.isErrorIndicatingAliveKvstore(tt.arg) != tt.want {
221 t.Errorf("isErrorIndicatingAliveKvstore failed for %s: expected %t but got %t", tt.name, tt.want, !tt.want)
222 }
223 })
224 }
225}
226
227func TestPut_EmbeddedEtcdServer(t *testing.T) {
228 backend := provisionBackendWithEmbeddedEtcdServer(t)
229 err := backend.Put("key1", []uint8("value1"))
230 assert.Nil(t, err)
231
232 // Assert alive state has become true
233 assert.True(t, backend.alive)
234
235 // Assert that kvstore has this value stored
236 kvpair, err := backend.Get("key1")
237 assert.NotNil(t, kvpair)
238 assert.Equal(t, defaultPathPrefix+"/key1", kvpair.Key)
239 assert.Equal(t, []uint8("value1"), kvpair.Value)
240
241 // Assert that Put overrides the Value
242 err = backend.Put("key1", []uint8("value11"))
243 assert.Nil(t, err)
244 kvpair, err = backend.Get("key1")
245 assert.NotNil(t, kvpair)
246 assert.Equal(t, defaultPathPrefix+"/key1", kvpair.Key)
247 assert.Equal(t, []uint8("value11"), kvpair.Value)
248}
249
250// Put operation should fail against Dummy Non-existent Etcd Server
251func TestPut_DummyEtcdServer(t *testing.T) {
252 backend := provisionBackendWithDummyEtcdServer(t)
253 err := backend.Put("key1", []uint8("value1"))
254 assert.NotNil(t, err)
255
256 // Assert alive state is still false
257 assert.False(t, backend.alive)
258}
259
260// Test Get for existing and non-existing key
261func TestGet_EmbeddedEtcdServer(t *testing.T) {
262 backend := provisionBackendWithEmbeddedEtcdServer(t)
263 err := backend.Put("key2", []uint8("value2"))
264
265 // Assert alive state has become true
266 assert.True(t, backend.alive)
267
268 // Assert that kvstore has this key stored
269 kvpair, err := backend.Get("key2")
270 assert.NotNil(t, kvpair)
271 assert.Nil(t, err)
272 assert.Equal(t, defaultPathPrefix+"/key2", kvpair.Key)
273 assert.Equal(t, []uint8("value2"), kvpair.Value)
274
275 // Assert that Get works fine for absent key3
276 kvpair, err = backend.Get("key3")
277 assert.Nil(t, kvpair)
278 assert.Nil(t, err) // no error as lookup is successful
279}
280
281// Get operation should fail against Dummy Non-existent Etcd Server
282func TestGet_DummyEtcdServer(t *testing.T) {
283 backend := provisionBackendWithDummyEtcdServer(t)
284 kvpair, err := backend.Get("key2")
285 assert.NotNil(t, err)
286 assert.Nil(t, kvpair)
287
288 // Assert alive state is still false
289 assert.False(t, backend.alive)
290}
291
292// Test Delete for existing and non-existing key
293func TestDelete_EmbeddedEtcdServer(t *testing.T) {
294 backend := provisionBackendWithEmbeddedEtcdServer(t)
295 err := backend.Put("key3", []uint8("value3"))
296
297 // Assert alive state has become true
298 assert.True(t, backend.alive)
299
300 // Assert that kvstore has this key stored
301 kvpair, err := backend.Get("key3")
302 assert.NotNil(t, kvpair)
303
304 // Delete and Assert that key has been removed
305 err = backend.Delete("key3")
306 assert.Nil(t, err)
307 kvpair, err = backend.Get("key3")
308 assert.Nil(t, kvpair)
309
310 // Assert that Delete silently ignores absent key3
311 err = backend.Delete("key3")
312 assert.Nil(t, err)
313}
314
315// Delete operation should fail against Dummy Non-existent Etcd Server
316func TestDelete_DummyEtcdServer(t *testing.T) {
317 backend := provisionBackendWithDummyEtcdServer(t)
318 err := backend.Delete("key3")
319 assert.NotNil(t, err)
320
321 // Assert alive state is still false
322 assert.False(t, backend.alive)
323}
324
325// Test List for series of values under a key path
326func TestList_EmbeddedEtcdServer(t *testing.T) {
327 key41 := "key4/subkey1"
328 key42 := "key4/subkey2"
329
330 backend := provisionBackendWithEmbeddedEtcdServer(t)
331 backend.Put(key41, []uint8("value4-1"))
332 backend.Put(key42, []uint8("value4-2"))
333
334 // Assert alive state has become true
335 assert.True(t, backend.alive)
336
337 // Assert that Get does not retrieve these Subkeys
338 kvpair, err := backend.Get("key4")
339 assert.Nil(t, kvpair)
340 assert.Nil(t, err)
341
342 // Assert that List operation retrieves these Child Keys
343 kvmap, err := backend.List("key4")
344 assert.NotNil(t, kvmap)
345 assert.Nil(t, err)
346 assert.Equal(t, 2, len(kvmap))
347 fullkey41 := defaultPathPrefix + "/" + key41
348 fullkey42 := defaultPathPrefix + "/" + key42
349 assert.Equal(t, fullkey41, kvmap[fullkey41].Key)
350 assert.Equal(t, []uint8("value4-1"), kvmap[fullkey41].Value)
351 assert.Equal(t, fullkey42, kvmap[fullkey42].Key)
352 assert.Equal(t, []uint8("value4-2"), kvmap[fullkey42].Value)
353}
354
355// List operation should fail against Dummy Non-existent Etcd Server
356func TestList_DummyEtcdServer(t *testing.T) {
357 backend := provisionBackendWithDummyEtcdServer(t)
358 kvmap, err := backend.List("key4")
359 assert.Nil(t, kvmap)
360 assert.NotNil(t, err)
361
362 // Assert alive state is still false
363 assert.False(t, backend.alive)
364}
365
366// Test Create and Delete Watch for Embedded Etcd Server
367func TestCreateWatch_EmbeddedEtcdServer(t *testing.T) {
368 backend := provisionBackendWithEmbeddedEtcdServer(t)
369 eventChan := backend.CreateWatch("key5")
370 assert.NotNil(t, eventChan)
371 assert.Equal(t, 0, len(eventChan))
372
373 // Assert this method does not change alive state
374 assert.False(t, backend.alive)
375
376 // Put a value for watched key and event should appear
377 err := backend.Put("key5", []uint8("value5"))
378 assert.Nil(t, err)
379 time.Sleep(time.Millisecond * 100)
380 assert.Equal(t, 1, len(eventChan))
381
382 backend.DeleteWatch("key5", eventChan)
383}