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