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