Skip to content

Commit a1a5db4

Browse files
author
Mathieu Tortuyaux
committed
lock/etcd_test: rewrite tests using etcd/v3
Signed-off-by: Mathieu Tortuyaux <mathieu@kinvolk.io>
1 parent fde90bd commit a1a5db4

File tree

1 file changed

+144
-105
lines changed

1 file changed

+144
-105
lines changed

lock/etcd_test.go

Lines changed: 144 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -16,132 +16,171 @@ package lock
1616

1717
import (
1818
"errors"
19-
"reflect"
2019
"testing"
2120

22-
"go.etcd.io/etcd/client"
21+
pb "go.etcd.io/etcd/api/v3/mvccpb"
22+
client "go.etcd.io/etcd/client/v3"
23+
24+
"github.com/stretchr/testify/assert"
25+
"github.com/stretchr/testify/require"
2326
"golang.org/x/net/context"
2427
)
2528

29+
// testEtcdClient is the struct used to mock the `etcd`
30+
// client
2631
type testEtcdClient struct {
27-
err error
28-
resp *client.Response
32+
err error
33+
putResp *client.PutResponse
34+
getResp *client.GetResponse
2935
}
3036

31-
func (t *testEtcdClient) Get(ctx context.Context, key string, opts *client.GetOptions) (*client.Response, error) {
32-
return t.resp, t.err
33-
}
37+
func (t *testEtcdClient) Put(ctx context.Context, key, val string, opts ...client.OpOption) (*client.PutResponse, error) {
38+
return t.putResp, t.err
3439

35-
func (t *testEtcdClient) Set(ctx context.Context, key, value string, opts *client.SetOptions) (*client.Response, error) {
36-
return t.resp, t.err
3740
}
3841

39-
func (t *testEtcdClient) Create(ctx context.Context, key, value string) (*client.Response, error) {
40-
return t.resp, t.err
42+
func (t *testEtcdClient) Get(ctx context.Context, key string, opts ...client.OpOption) (*client.GetResponse, error) {
43+
return t.getResp, t.err
44+
4145
}
4246

4347
func TestEtcdLockClientInit(t *testing.T) {
44-
for i, tt := range []struct {
45-
ee error
46-
want bool
47-
group string
48-
keypath string
49-
}{
50-
{nil, false, "", SemaphorePrefix},
51-
{client.Error{Code: client.ErrorCodeNodeExist}, false, "", SemaphorePrefix},
52-
{client.Error{Code: client.ErrorCodeKeyNotFound}, true, "", SemaphorePrefix},
53-
{errors.New("some random error"), true, "", SemaphorePrefix},
54-
{client.Error{Code: client.ErrorCodeKeyNotFound}, true, "database", "coreos.com/updateengine/rebootlock/groups/database/semaphore"},
55-
{nil, false, "prod/database", "coreos.com/updateengine/rebootlock/groups/prod%2Fdatabase/semaphore"},
56-
} {
57-
elc, got := NewEtcdLockClient(&testEtcdClient{err: tt.ee}, tt.group)
58-
if (got != nil) != tt.want {
59-
t.Errorf("case %d: unexpected error state initializing Client: got %v", i, got)
60-
continue
61-
}
62-
63-
if got != nil {
64-
continue
65-
}
66-
67-
if elc.keypath != tt.keypath {
68-
t.Errorf("case %d: unexpected etcd key path: got %v want %v", i, elc.keypath, tt.keypath)
69-
}
70-
}
71-
}
48+
t.Run("Success", func(t *testing.T) {
49+
elc, err := NewEtcdLockClient(&testEtcdClient{
50+
err: nil,
51+
getResp: &client.GetResponse{Count: 0},
52+
putResp: &client.PutResponse{},
53+
},
54+
"",
55+
)
56+
require.Nil(t, err)
7257

73-
func makeResponse(idx int, val string) *client.Response {
74-
return &client.Response{
75-
Node: &client.Node{
76-
Value: val,
77-
ModifiedIndex: uint64(idx),
58+
assert.NotNil(t, elc)
59+
})
60+
t.Run("SuccessWithExistingValue", func(t *testing.T) {
61+
elc, err := NewEtcdLockClient(&testEtcdClient{
62+
err: nil,
63+
getResp: &client.GetResponse{
64+
Count: 1,
65+
Kvs: []*pb.KeyValue{
66+
&pb.KeyValue{
67+
Key: []byte(SemaphorePrefix),
68+
Value: []byte(`{"semaphore": 1, "max": 2, "holders": ["foo", "bar"]}`),
69+
},
70+
},
71+
},
72+
putResp: &client.PutResponse{},
7873
},
79-
}
74+
"",
75+
)
76+
require.Nil(t, err)
77+
78+
assert.NotNil(t, elc)
79+
})
80+
t.Run("Error", func(t *testing.T) {
81+
elc, err := NewEtcdLockClient(&testEtcdClient{
82+
err: errors.New("connection refused"),
83+
getResp: &client.GetResponse{Count: 0},
84+
putResp: &client.PutResponse{},
85+
},
86+
"",
87+
)
88+
require.Nil(t, elc)
89+
90+
assert.Equal(t, "unable to init etcd lock client: unable to get semaphore: connection refused", err.Error())
91+
})
8092
}
8193

8294
func TestEtcdLockClientGet(t *testing.T) {
83-
for i, tt := range []struct {
84-
ee error
85-
er *client.Response
86-
ws *Semaphore
87-
we bool
88-
}{
89-
// errors returned from etcd
90-
{errors.New("some error"), nil, nil, true},
91-
{client.Error{Code: client.ErrorCodeKeyNotFound}, nil, nil, true},
92-
// bad JSON should cause errors
93-
{nil, makeResponse(0, "asdf"), nil, true},
94-
{nil, makeResponse(0, `{"semaphore:`), nil, true},
95-
// successful calls
96-
{nil, makeResponse(10, `{"semaphore": 1}`), &Semaphore{Index: 10, Semaphore: 1}, false},
97-
{nil, makeResponse(1024, `{"semaphore": 1, "max": 2, "holders": ["foo", "bar"]}`), &Semaphore{Index: 1024, Semaphore: 1, Max: 2, Holders: []string{"foo", "bar"}}, false},
98-
// index should be set from etcd, not json!
99-
{nil, makeResponse(1234, `{"semaphore": 89, "index": 4567}`), &Semaphore{Index: 1234, Semaphore: 89}, false},
100-
} {
101-
elc := &EtcdLockClient{
102-
keyapi: &testEtcdClient{
103-
err: tt.ee,
104-
resp: tt.er,
95+
t.Run("Success", func(t *testing.T) {
96+
elc, err := NewEtcdLockClient(&testEtcdClient{
97+
err: nil,
98+
putResp: &client.PutResponse{},
99+
getResp: &client.GetResponse{
100+
Count: 1,
101+
Kvs: []*pb.KeyValue{
102+
&pb.KeyValue{
103+
Key: []byte(SemaphorePrefix),
104+
// index should be set from etcd, not json (backported from legacy test)
105+
Value: []byte(`{"index": 12, "semaphore": 1, "max": 2, "holders": ["foo", "bar"]}`),
106+
Version: 1234,
107+
},
108+
},
109+
},
110+
},
111+
"",
112+
)
113+
require.Nil(t, err)
114+
115+
res, err := elc.Get()
116+
require.Nil(t, err)
117+
118+
assert.Equal(t, 1, res.Semaphore)
119+
assert.Equal(t, uint64(1234), res.Index)
120+
})
121+
t.Run("SuccessNotFound", func(t *testing.T) {
122+
elc, err := NewEtcdLockClient(&testEtcdClient{
123+
err: nil,
124+
getResp: &client.GetResponse{Count: 0},
125+
putResp: &client.PutResponse{},
126+
},
127+
"",
128+
)
129+
require.Nil(t, err)
130+
131+
res, err := elc.Get()
132+
require.Nil(t, res)
133+
assert.ErrorIs(t, err, ErrNotFound)
134+
})
135+
t.Run("ErrorWithMalformedJSON", func(t *testing.T) {
136+
elc, err := NewEtcdLockClient(&testEtcdClient{
137+
err: nil,
138+
getResp: &client.GetResponse{
139+
Count: 1,
140+
Kvs: []*pb.KeyValue{
141+
&pb.KeyValue{
142+
Key: []byte(SemaphorePrefix),
143+
// notice the missing `,` in the array
144+
Value: []byte(`{"semaphore": 1, "max": 2, "holders": ["foo" "bar"]}`),
145+
},
146+
},
105147
},
106-
}
107-
gs, ge := elc.Get()
108-
if tt.we {
109-
if ge == nil {
110-
t.Fatalf("case %d: expected error but got nil!", i)
111-
}
112-
} else {
113-
if ge != nil {
114-
t.Fatalf("case %d: unexpected error: %v", i, ge)
115-
}
116-
}
117-
if !reflect.DeepEqual(gs, tt.ws) {
118-
t.Fatalf("case %d: bad semaphore: got %#v, want %#v", i, gs, tt.ws)
119-
}
120-
}
148+
putResp: &client.PutResponse{},
149+
},
150+
"",
151+
)
152+
require.Nil(t, elc)
153+
154+
assert.Equal(t, "unable to init etcd lock client: unable to get semaphore: invalid character '\"' after array element", err.Error())
155+
})
121156
}
122157

123158
func TestEtcdLockClientSet(t *testing.T) {
124-
for i, tt := range []struct {
125-
sem *Semaphore
126-
ee error // error returned from etcd
127-
want bool // do we expect Set to return an error
128-
}{
129-
// nil semaphore cannot be set
130-
{nil, nil, true},
131-
// empty semaphore is OK
132-
{&Semaphore{}, nil, false},
133-
{&Semaphore{Index: uint64(1234)}, nil, false},
134-
// all errors returned from etcd should propagate
135-
{&Semaphore{}, client.Error{Code: client.ErrorCodeNodeExist}, true},
136-
{&Semaphore{}, client.Error{Code: client.ErrorCodeKeyNotFound}, true},
137-
{&Semaphore{}, errors.New("some random error"), true},
138-
} {
139-
elc := &EtcdLockClient{
140-
keyapi: &testEtcdClient{err: tt.ee},
141-
}
142-
got := elc.Set(tt.sem)
143-
if (got != nil) != tt.want {
144-
t.Errorf("case %d: unexpected error state calling Set: got %v", i, got)
145-
}
146-
}
159+
t.Run("Success", func(t *testing.T) {
160+
elc, err := NewEtcdLockClient(&testEtcdClient{
161+
err: nil,
162+
getResp: &client.GetResponse{Count: 0},
163+
putResp: &client.PutResponse{},
164+
},
165+
"",
166+
)
167+
require.Nil(t, err)
168+
169+
err = elc.Set(&Semaphore{})
170+
assert.Nil(t, err)
171+
172+
})
173+
t.Run("ErrorNilSemaphore", func(t *testing.T) {
174+
elc, err := NewEtcdLockClient(&testEtcdClient{
175+
err: nil,
176+
getResp: &client.GetResponse{Count: 0},
177+
putResp: &client.PutResponse{},
178+
},
179+
"",
180+
)
181+
require.Nil(t, err)
182+
183+
err = elc.Set(nil)
184+
assert.Equal(t, "cannot set nil semaphore", err.Error())
185+
})
147186
}

0 commit comments

Comments
 (0)