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