1 package etcd
2
3 import (
4 "context"
5 "errors"
6 "reflect"
7 "testing"
8 "time"
9
10 etcd "go.etcd.io/etcd/client/v2"
11 )
12
13 func TestNewClient(t *testing.T) {
14 client, err := NewClient(
15 context.Background(),
16 []string{"http://irrelevant:12345"},
17 ClientOptions{
18 DialTimeout: 2 * time.Second,
19 DialKeepAlive: 2 * time.Second,
20 HeaderTimeoutPerRequest: 2 * time.Second,
21 },
22 )
23 if err != nil {
24 t.Fatalf("unexpected error creating client: %v", err)
25 }
26 if client == nil {
27 t.Fatal("expected new Client, got nil")
28 }
29 }
30
31
32 func TestOptions(t *testing.T) {
33 a, err := NewClient(
34 context.Background(),
35 []string{},
36 ClientOptions{
37 Cert: "",
38 Key: "",
39 CACert: "",
40 DialTimeout: 2 * time.Second,
41 DialKeepAlive: 2 * time.Second,
42 HeaderTimeoutPerRequest: 2 * time.Second,
43 },
44 )
45 if err == nil {
46 t.Errorf("expected error: %v", err)
47 }
48 if a != nil {
49 t.Fatalf("expected client to be nil on failure")
50 }
51
52 _, err = NewClient(
53 context.Background(),
54 []string{"http://irrelevant:12345"},
55 ClientOptions{
56 Cert: "blank.crt",
57 Key: "blank.key",
58 CACert: "blank.CACert",
59 DialTimeout: 2 * time.Second,
60 DialKeepAlive: 2 * time.Second,
61 HeaderTimeoutPerRequest: 2 * time.Second,
62 },
63 )
64 if err == nil {
65 t.Errorf("expected error: %v", err)
66 }
67 }
68
69
70
71
72
73 type fakeKeysAPI struct {
74 event chan bool
75 err chan bool
76 getres *getResult
77 }
78
79 type getResult struct {
80 resp *etcd.Response
81 err error
82 }
83
84
85 func (fka *fakeKeysAPI) Get(ctx context.Context, key string, opts *etcd.GetOptions) (*etcd.Response, error) {
86 if fka.getres == nil {
87 return nil, nil
88 }
89 return fka.getres.resp, fka.getres.err
90 }
91
92
93 func (fka *fakeKeysAPI) Set(ctx context.Context, key, value string, opts *etcd.SetOptions) (*etcd.Response, error) {
94 return nil, nil
95 }
96
97
98 func (fka *fakeKeysAPI) Delete(ctx context.Context, key string, opts *etcd.DeleteOptions) (*etcd.Response, error) {
99 return nil, nil
100 }
101
102
103 func (fka *fakeKeysAPI) Create(ctx context.Context, key, value string) (*etcd.Response, error) {
104 return nil, nil
105 }
106
107
108 func (fka *fakeKeysAPI) CreateInOrder(ctx context.Context, dir, value string, opts *etcd.CreateInOrderOptions) (*etcd.Response, error) {
109 return nil, nil
110 }
111
112
113 func (fka *fakeKeysAPI) Update(ctx context.Context, key, value string) (*etcd.Response, error) {
114 return nil, nil
115 }
116
117
118 func (fka *fakeKeysAPI) Watcher(key string, opts *etcd.WatcherOptions) etcd.Watcher {
119 return &fakeWatcher{fka.event, fka.err}
120 }
121
122
123 type fakeWatcher struct {
124 event chan bool
125 err chan bool
126 }
127
128
129
130
131 func (fw *fakeWatcher) Next(context.Context) (*etcd.Response, error) {
132 select {
133 case <-fw.event:
134 return nil, nil
135 case <-fw.err:
136 return nil, errors.New("error from underlying etcd watcher")
137
138 }
139 }
140
141
142 func newFakeClient(event, err chan bool, getres *getResult) Client {
143 return &client{
144 keysAPI: &fakeKeysAPI{event, err, getres},
145 ctx: context.Background(),
146 }
147 }
148
149
150 func TestRegisterClient(t *testing.T) {
151 client := newFakeClient(nil, nil, nil)
152
153 err := client.Register(Service{Key: "", Value: "value", DeleteOptions: nil})
154 if want, have := ErrNoKey, err; want != have {
155 t.Fatalf("want %v, have %v", want, have)
156 }
157
158 err = client.Register(Service{Key: "key", Value: "", DeleteOptions: nil})
159 if want, have := ErrNoValue, err; want != have {
160 t.Fatalf("want %v, have %v", want, have)
161 }
162
163 err = client.Register(Service{Key: "key", Value: "value", DeleteOptions: nil})
164 if err != nil {
165 t.Fatal(err)
166 }
167 }
168
169
170 func TestDeregisterClient(t *testing.T) {
171 client := newFakeClient(nil, nil, nil)
172
173 err := client.Deregister(Service{Key: "", Value: "value", DeleteOptions: nil})
174 if want, have := ErrNoKey, err; want != have {
175 t.Fatalf("want %v, have %v", want, have)
176 }
177
178 err = client.Deregister(Service{Key: "key", Value: "", DeleteOptions: nil})
179 if err != nil {
180 t.Fatal(err)
181 }
182 }
183
184
185
186 func TestWatchPrefix(t *testing.T) {
187 err := make(chan bool)
188 event := make(chan bool)
189 watchPrefixReturned := make(chan bool, 1)
190 client := newFakeClient(event, err, nil)
191
192 ch := make(chan struct{})
193 go func() {
194 client.WatchPrefix("prefix", ch)
195 watchPrefixReturned <- true
196 }()
197
198
199
200 <-ch
201
202
203 event <- true
204 if want, have := struct{}{}, <-ch; want != have {
205 t.Fatalf("want %v, have %v", want, have)
206 }
207
208
209 err <- true
210 select {
211 case <-watchPrefixReturned:
212 break
213 case <-time.After(1 * time.Second):
214 t.Fatal("WatchPrefix not returning on errors")
215 }
216 }
217
218 var errKeyAPI = errors.New("emulate error returned by KeysAPI.Get")
219
220
221 var getEntriesTestTable = []struct {
222 input getResult
223 resp []string
224 err error
225
226 }{
227
228 {getResult{nil, errKeyAPI}, nil, errKeyAPI},
229
230 {getResult{&etcd.Response{
231 Action: "get",
232 Node: &etcd.Node{
233 Key: "nodekey",
234 Dir: false,
235 Value: "",
236 Nodes: nil,
237 CreatedIndex: 0,
238 ModifiedIndex: 0,
239 Expiration: nil,
240 TTL: 0,
241 },
242 PrevNode: nil,
243 Index: 0,
244 }, nil}, []string{}, nil},
245
246 {getResult{&etcd.Response{
247 Action: "get",
248 Node: &etcd.Node{
249 Key: "nodekey",
250 Dir: false,
251 Value: "nodevalue",
252 Nodes: nil,
253 CreatedIndex: 0,
254 ModifiedIndex: 0,
255 Expiration: nil,
256 TTL: 0,
257 },
258 PrevNode: nil,
259 Index: 0,
260 }, nil}, []string{"nodevalue"}, nil},
261
262 {getResult{&etcd.Response{
263 Action: "get",
264 Node: &etcd.Node{
265 Key: "nodekey",
266 Dir: true,
267 Value: "nodevalue",
268 Nodes: []*etcd.Node{
269 {
270 Key: "childnode1",
271 Dir: false,
272 Value: "childvalue1",
273 Nodes: nil,
274 CreatedIndex: 0,
275 ModifiedIndex: 0,
276 Expiration: nil,
277 TTL: 0,
278 },
279 {
280 Key: "childnode2",
281 Dir: false,
282 Value: "childvalue2",
283 Nodes: nil,
284 CreatedIndex: 0,
285 ModifiedIndex: 0,
286 Expiration: nil,
287 TTL: 0,
288 },
289 },
290 CreatedIndex: 0,
291 ModifiedIndex: 0,
292 Expiration: nil,
293 TTL: 0,
294 },
295 PrevNode: nil,
296 Index: 0,
297 }, nil}, []string{"childvalue1", "childvalue2"}, nil},
298 }
299
300 func TestGetEntries(t *testing.T) {
301 for _, et := range getEntriesTestTable {
302 client := newFakeClient(nil, nil, &et.input)
303 resp, err := client.GetEntries("prefix")
304 if want, have := et.resp, resp; !reflect.DeepEqual(want, have) {
305 t.Fatalf("want %v, have %v", want, have)
306 }
307 if want, have := et.err, err; want != have {
308 t.Fatalf("want %v, have %v", want, have)
309 }
310 }
311 }
312
View as plain text