1package dependency
2
3import (
4	"fmt"
5	"testing"
6
7	"github.com/hashicorp/consul/api"
8	"github.com/stretchr/testify/assert"
9)
10
11func TestNewHealthServiceQuery(t *testing.T) {
12	t.Parallel()
13
14	cases := []struct {
15		name string
16		i    string
17		exp  *HealthServiceQuery
18		err  bool
19	}{
20		{
21			"empty",
22			"",
23			nil,
24			true,
25		},
26		{
27			"dc_only",
28			"@dc1",
29			nil,
30			true,
31		},
32		{
33			"near_only",
34			"~near",
35			nil,
36			true,
37		},
38		{
39			"tag_only",
40			"tag.",
41			nil,
42			true,
43		},
44		{
45			"name",
46			"name",
47			&HealthServiceQuery{
48				filters: []string{"passing"},
49				name:    "name",
50			},
51			false,
52		},
53		{
54			"name_dc",
55			"name@dc1",
56			&HealthServiceQuery{
57				dc:      "dc1",
58				filters: []string{"passing"},
59				name:    "name",
60			},
61			false,
62		},
63		{
64			"name_dc_near",
65			"name@dc1~near",
66			&HealthServiceQuery{
67				dc:      "dc1",
68				filters: []string{"passing"},
69				name:    "name",
70				near:    "near",
71			},
72			false,
73		},
74		{
75			"name_near",
76			"name~near",
77			&HealthServiceQuery{
78				filters: []string{"passing"},
79				name:    "name",
80				near:    "near",
81			},
82			false,
83		},
84		{
85			"tag_name",
86			"tag.name",
87			&HealthServiceQuery{
88				filters: []string{"passing"},
89				name:    "name",
90				tag:     "tag",
91			},
92			false,
93		},
94		{
95			"tag_name_dc",
96			"tag.name@dc",
97			&HealthServiceQuery{
98				dc:      "dc",
99				filters: []string{"passing"},
100				name:    "name",
101				tag:     "tag",
102			},
103			false,
104		},
105		{
106			"tag_name_near",
107			"tag.name~near",
108			&HealthServiceQuery{
109				filters: []string{"passing"},
110				name:    "name",
111				near:    "near",
112				tag:     "tag",
113			},
114			false,
115		},
116		{
117			"tag_name_dc_near",
118			"tag.name@dc~near",
119			&HealthServiceQuery{
120				dc:      "dc",
121				filters: []string{"passing"},
122				name:    "name",
123				near:    "near",
124				tag:     "tag",
125			},
126			false,
127		},
128	}
129
130	for i, tc := range cases {
131		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
132			act, err := NewHealthServiceQuery(tc.i)
133			if (err != nil) != tc.err {
134				t.Fatal(err)
135			}
136
137			if act != nil {
138				act.stopCh = nil
139			}
140
141			assert.Equal(t, tc.exp, act)
142		})
143	}
144	// Connect
145	// all tests above also test connect, just need to check enabling it
146	t.Run("connect_query", func(t *testing.T) {
147		act, err := NewHealthConnectQuery("name")
148		if err != nil {
149			t.Fatal(err)
150		}
151		if act != nil {
152			act.stopCh = nil
153		}
154		exp := &HealthServiceQuery{
155			filters: []string{"passing"},
156			name:    "name",
157			connect: true,
158		}
159
160		assert.Equal(t, exp, act)
161	})
162}
163
164func TestHealthConnectServiceQuery_Fetch(t *testing.T) {
165	t.Parallel()
166	cases := []struct {
167		name string
168		in   string
169		exp  []*HealthService
170	}{
171		{
172			"connect-service",
173			"foo",
174			[]*HealthService{
175				&HealthService{
176					Name:        "foo-sidecar-proxy",
177					ID:          "foo",
178					Port:        21999,
179					Status:      "passing",
180					Address:     "127.0.0.1",
181					NodeAddress: "127.0.0.1",
182					Tags:        ServiceTags([]string{}),
183					NodeMeta: map[string]string{
184						"consul-network-segment": ""},
185					Weights: api.AgentWeights{
186						Passing: 1,
187						Warning: 1,
188					},
189				},
190			},
191		},
192	}
193	for i, tc := range cases {
194		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
195			d, err := NewHealthConnectQuery(tc.in)
196			if err != nil {
197				t.Fatal(err)
198			}
199			defer func() {
200				d.Stop()
201			}()
202			res, _, err := d.Fetch(testClients, nil)
203			if err != nil {
204				t.Fatal(err)
205			}
206			var act []*HealthService
207			if act = res.([]*HealthService); len(act) != 1 {
208				t.Fatal("Expected 1 result, got ", len(act))
209			}
210			// blank out fields we don't want to test
211			inst := act[0]
212			inst.Node, inst.NodeID = "", ""
213			inst.Checks = nil
214			inst.NodeTaggedAddresses = nil
215
216			assert.Equal(t, tc.exp, act)
217		})
218	}
219}
220
221func TestHealthServiceQuery_Fetch(t *testing.T) {
222	t.Parallel()
223
224	cases := []struct {
225		name string
226		i    string
227		exp  []*HealthService
228	}{
229		{
230			"consul",
231			"consul",
232			[]*HealthService{
233				&HealthService{
234					Node:        testConsul.Config.NodeName,
235					NodeAddress: testConsul.Config.Bind,
236					NodeTaggedAddresses: map[string]string{
237						"lan": "127.0.0.1",
238						"wan": "127.0.0.1",
239					},
240					NodeMeta: map[string]string{
241						"consul-network-segment": "",
242					},
243					ServiceMeta: map[string]string{},
244					Address:     testConsul.Config.Bind,
245					ID:          "consul",
246					Name:        "consul",
247					Tags:        []string{},
248					Status:      "passing",
249					Port:        testConsul.Config.Ports.Server,
250					Weights: api.AgentWeights{
251						Passing: 1,
252						Warning: 1,
253					},
254				},
255			},
256		},
257		{
258			"filters",
259			"consul|warning",
260			[]*HealthService{},
261		},
262		{
263			"multifilter",
264			"consul|warning,passing",
265			[]*HealthService{
266				&HealthService{
267					Node:        testConsul.Config.NodeName,
268					NodeAddress: testConsul.Config.Bind,
269					NodeTaggedAddresses: map[string]string{
270						"lan": "127.0.0.1",
271						"wan": "127.0.0.1",
272					},
273					NodeMeta: map[string]string{
274						"consul-network-segment": "",
275					},
276					ServiceMeta: map[string]string{},
277					Address:     testConsul.Config.Bind,
278					ID:          "consul",
279					Name:        "consul",
280					Tags:        []string{},
281					Status:      "passing",
282					Port:        testConsul.Config.Ports.Server,
283					Weights: api.AgentWeights{
284						Passing: 1,
285						Warning: 1,
286					},
287				},
288			},
289		},
290		{
291			"service-meta",
292			"service-meta",
293			[]*HealthService{
294				&HealthService{
295					Node:        testConsul.Config.NodeName,
296					NodeAddress: testConsul.Config.Bind,
297					NodeTaggedAddresses: map[string]string{
298						"lan": "127.0.0.1",
299						"wan": "127.0.0.1",
300					},
301					NodeMeta: map[string]string{
302						"consul-network-segment": "",
303					},
304					ServiceMeta: map[string]string{
305						"meta1": "value1",
306					},
307					Address: testConsul.Config.Bind,
308					ID:      "service-meta",
309					Name:    "service-meta",
310					Tags:    []string{"tag1"},
311					Status:  "passing",
312					Weights: api.AgentWeights{
313						Passing: 1,
314						Warning: 1,
315					},
316				},
317			},
318		},
319		{
320			"service-taggedAddresses",
321			"service-taggedAddresses",
322			[]*HealthService{
323				&HealthService{
324					Node:        testConsul.Config.NodeName,
325					NodeAddress: testConsul.Config.Bind,
326					NodeTaggedAddresses: map[string]string{
327						"lan": "127.0.0.1",
328						"wan": "127.0.0.1",
329					},
330					NodeMeta: map[string]string{
331						"consul-network-segment": "",
332					},
333					ServiceMeta: map[string]string{},
334					Address:     testConsul.Config.Bind,
335					ServiceTaggedAddresses: map[string]api.ServiceAddress{
336						"lan": {
337							Address: "192.0.2.1",
338							Port:    80,
339						},
340						"wan": {
341							Address: "192.0.2.2",
342							Port:    443,
343						},
344					},
345					ID:     "service-taggedAddresses",
346					Name:   "service-taggedAddresses",
347					Tags:   []string{},
348					Status: "passing",
349					Weights: api.AgentWeights{
350						Passing: 1,
351						Warning: 1,
352					},
353				},
354			},
355		},
356	}
357
358	for i, tc := range cases {
359		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
360			d, err := NewHealthServiceQuery(tc.i)
361			if err != nil {
362				t.Fatal(err)
363			}
364
365			act, _, err := d.Fetch(testClients, nil)
366			if err != nil {
367				t.Fatal(err)
368			}
369
370			if act != nil {
371				for _, v := range act.([]*HealthService) {
372					v.NodeID = ""
373					v.Checks = nil
374					// delete any version data from ServiceMeta
375					v.ServiceMeta = filterVersionMeta(v.ServiceMeta)
376					v.NodeTaggedAddresses = filterAddresses(
377						v.NodeTaggedAddresses)
378				}
379			}
380
381			assert.Equal(t, tc.exp, act)
382		})
383	}
384}
385
386func TestHealthServiceQuery_String(t *testing.T) {
387	t.Parallel()
388
389	cases := []struct {
390		name string
391		i    string
392		exp  string
393	}{
394		{
395			"name",
396			"name",
397			"health.service(name|passing)",
398		},
399		{
400			"name_dc",
401			"name@dc",
402			"health.service(name@dc|passing)",
403		},
404		{
405			"name_filter",
406			"name|any",
407			"health.service(name|any)",
408		},
409		{
410			"name_multifilter",
411			"name|warning,passing",
412			"health.service(name|passing,warning)",
413		},
414		{
415			"name_near",
416			"name~near",
417			"health.service(name~near|passing)",
418		},
419		{
420			"name_near_filter",
421			"name~near|any",
422			"health.service(name~near|any)",
423		},
424		{
425			"name_dc_near",
426			"name@dc~near",
427			"health.service(name@dc~near|passing)",
428		},
429		{
430			"name_dc_near_filter",
431			"name@dc~near|any",
432			"health.service(name@dc~near|any)",
433		},
434		{
435			"tag_name",
436			"tag.name",
437			"health.service(tag.name|passing)",
438		},
439		{
440			"tag_name_dc",
441			"tag.name@dc",
442			"health.service(tag.name@dc|passing)",
443		},
444		{
445			"tag_name_near",
446			"tag.name~near",
447			"health.service(tag.name~near|passing)",
448		},
449		{
450			"tag_name_dc_near",
451			"tag.name@dc~near",
452			"health.service(tag.name@dc~near|passing)",
453		},
454	}
455
456	for i, tc := range cases {
457		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
458			d, err := NewHealthServiceQuery(tc.i)
459			if err != nil {
460				t.Fatal(err)
461			}
462			assert.Equal(t, tc.exp, d.String())
463		})
464	}
465}
466
467func TestHealthServiceQueryConnect_String(t *testing.T) {
468	t.Parallel()
469
470	cases := []struct {
471		name string
472		fact func(string) (*HealthServiceQuery, error)
473		in   string
474		exp  string
475	}{
476		{
477			"name",
478			NewHealthServiceQuery,
479			"name",
480			"health.service(name|passing)",
481		},
482		{
483			"name",
484			NewHealthConnectQuery,
485			"name",
486			"health.connect(name|passing)",
487		},
488	}
489
490	for i, tc := range cases {
491		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
492			d, err := tc.fact(tc.in)
493			if err != nil {
494				t.Fatal(err)
495			}
496			assert.Equal(t, tc.exp, d.String())
497		})
498	}
499}
500