master
go 364 lines 9.05 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package openldap
4
5 import (
6 "context"
7 "errors"
8 "fmt"
9 "os"
10 "testing"
11
12 "github.com/go-ldap/ldap/v3"
13 "github.com/stretchr/testify/assert"
14 "github.com/stretchr/testify/require"
15
16 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/collecttest"
17 )
18
19 var (
20 dataConfigJSON, _ = os.ReadFile("testdata/config.json")
21 dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
22 )
23
24 func Test_testDataIsValid(t *testing.T) {
25 for name, data := range map[string][]byte{
26 "dataConfigJSON": dataConfigJSON,
27 "dataConfigYAML": dataConfigYAML,
28 } {
29 assert.NotNil(t, data, name)
30 }
31 }
32
33 func TestCollector_ConfigurationSerialize(t *testing.T) {
34 collecttest.TestConfigurationSerialize(t, &Collector{}, dataConfigJSON, dataConfigYAML)
35 }
36
37 func TestCollector_Init(t *testing.T) {
38 tests := map[string]struct {
39 config Config
40 wantFail bool
41 }{
42 "fails with default config": {
43 wantFail: true,
44 config: New().Config,
45 },
46 "fails if URL not set": {
47 wantFail: true,
48 config: func() Config {
49 conf := New().Config
50 conf.URL = ""
51 return conf
52 }(),
53 },
54 }
55
56 for name, test := range tests {
57 t.Run(name, func(t *testing.T) {
58 collr := New()
59 collr.Config = test.config
60
61 if test.wantFail {
62 assert.Error(t, collr.Init(context.Background()))
63 } else {
64 assert.NoError(t, collr.Init(context.Background()))
65 }
66 })
67 }
68 }
69
70 func TestCollector_Cleanup(t *testing.T) {
71 tests := map[string]struct {
72 prepare func() *Collector
73 }{
74 "not initialized": {
75 prepare: func() *Collector {
76 return New()
77 },
78 },
79 "after check": {
80 prepare: func() *Collector {
81 collr := New()
82 collr.newConn = func(Config) ldapConn { return prepareMockOk() }
83 _ = collr.Check(context.Background())
84 return collr
85 },
86 },
87 "after collect": {
88 prepare: func() *Collector {
89 collr := New()
90 collr.newConn = func(Config) ldapConn { return prepareMockOk() }
91 _ = collr.Collect(context.Background())
92 return collr
93 },
94 },
95 }
96
97 for name, test := range tests {
98 t.Run(name, func(t *testing.T) {
99 collr := test.prepare()
100
101 assert.NotPanics(t, func() { collr.Cleanup(context.Background()) })
102 })
103 }
104 }
105
106 func TestCollector_Charts(t *testing.T) {
107 assert.NotNil(t, New().Charts())
108 }
109
110 func TestCollector_Check(t *testing.T) {
111 tests := map[string]struct {
112 prepareMock func() *mockOpenLDAPConn
113 wantFail bool
114 }{
115 "success case": {
116 wantFail: false,
117 prepareMock: prepareMockOk,
118 },
119 "err on connect": {
120 wantFail: true,
121 prepareMock: prepareMockErrOnConnect,
122 },
123 "err on search": {
124 wantFail: true,
125 prepareMock: prepareMockErrOnSearch,
126 },
127 }
128
129 for name, test := range tests {
130 t.Run(name, func(t *testing.T) {
131 collr := New()
132 mock := test.prepareMock()
133 collr.newConn = func(Config) ldapConn { return mock }
134
135 if test.wantFail {
136 assert.Error(t, collr.Check(context.Background()))
137 } else {
138 assert.NoError(t, collr.Check(context.Background()))
139 }
140 })
141 }
142 }
143
144 func TestCollector_Collect(t *testing.T) {
145 tests := map[string]struct {
146 prepareMock func() *mockOpenLDAPConn
147 wantMetrics map[string]int64
148 disconnectBeforeCleanup bool
149 disconnectAfterCleanup bool
150 }{
151 "success case": {
152 prepareMock: prepareMockOk,
153 disconnectBeforeCleanup: false,
154 disconnectAfterCleanup: true,
155 wantMetrics: map[string]int64{
156 "bytes_sent": 1,
157 "completed_add_operations": 1,
158 "completed_bind_operations": 1,
159 "completed_compare_operations": 1,
160 "completed_delete_operations": 1,
161 "completed_modify_operations": 1,
162 "completed_operations": 7,
163 "completed_search_operations": 1,
164 "completed_unbind_operations": 1,
165 "current_connections": 1,
166 "entries_sent": 1,
167 "initiated_add_operations": 1,
168 "initiated_bind_operations": 1,
169 "initiated_compare_operations": 1,
170 "initiated_delete_operations": 1,
171 "initiated_modify_operations": 1,
172 "initiated_operations": 7,
173 "initiated_search_operations": 1,
174 "initiated_unbind_operations": 1,
175 "read_waiters": 1,
176 "referrals_sent": 1,
177 "total_connections": 1,
178 "write_waiters": 1,
179 },
180 },
181 "err on connect": {
182 prepareMock: prepareMockErrOnConnect,
183 disconnectBeforeCleanup: false,
184 disconnectAfterCleanup: false,
185 },
186 "err on search": {
187 prepareMock: prepareMockErrOnSearch,
188 disconnectBeforeCleanup: true,
189 disconnectAfterCleanup: true,
190 },
191 }
192
193 for name, test := range tests {
194 t.Run(name, func(t *testing.T) {
195 collr := New()
196 mock := test.prepareMock()
197 collr.newConn = func(Config) ldapConn { return mock }
198
199 mx := collr.Collect(context.Background())
200
201 require.Equal(t, test.wantMetrics, mx)
202
203 if len(test.wantMetrics) > 0 {
204 collecttest.TestMetricsHasAllChartsDims(t, collr.Charts(), mx)
205 }
206
207 assert.Equal(t, test.disconnectBeforeCleanup, mock.disconnectCalled, "disconnect before cleanup")
208 collr.Cleanup(context.Background())
209 assert.Equal(t, test.disconnectAfterCleanup, mock.disconnectCalled, "disconnect after cleanup")
210 })
211 }
212 }
213
214 func prepareMockOk() *mockOpenLDAPConn {
215 return &mockOpenLDAPConn{
216 dataSearchMonCounters: &ldap.SearchResult{
217 Entries: []*ldap.Entry{
218 {
219 DN: "cn=Current,cn=Connections,cn=Monitor",
220 Attributes: []*ldap.EntryAttribute{
221 {Name: attrMonitorCounter, Values: []string{"1"}},
222 },
223 },
224 {
225 DN: "cn=Total,cn=Connections,cn=Monitor",
226 Attributes: []*ldap.EntryAttribute{
227 {Name: attrMonitorCounter, Values: []string{"1"}},
228 },
229 },
230 {
231 DN: "cn=Bytes,cn=Statistics,cn=Monitor",
232 Attributes: []*ldap.EntryAttribute{
233 {Name: attrMonitorCounter, Values: []string{"1"}},
234 },
235 },
236 {
237 DN: "cn=Referrals,cn=Statistics,cn=Monitor",
238 Attributes: []*ldap.EntryAttribute{
239 {Name: attrMonitorCounter, Values: []string{"1"}},
240 },
241 },
242 {
243 DN: "cn=Entries,cn=Statistics,cn=Monitor",
244 Attributes: []*ldap.EntryAttribute{
245 {Name: attrMonitorCounter, Values: []string{"1"}},
246 },
247 },
248 {
249 DN: "cn=Write,cn=Waiters,cn=Monitor",
250 Attributes: []*ldap.EntryAttribute{
251 {Name: attrMonitorCounter, Values: []string{"1"}},
252 },
253 },
254 {
255 DN: "cn=Read,cn=Waiters,cn=Monitor",
256 Attributes: []*ldap.EntryAttribute{
257 {Name: attrMonitorCounter, Values: []string{"1"}},
258 },
259 },
260 },
261 },
262 dataSearchMonOperations: &ldap.SearchResult{
263 Entries: []*ldap.Entry{
264 {
265 DN: "cn=Bind,cn=Operations,cn=Monitor",
266 Attributes: []*ldap.EntryAttribute{
267 {Name: attrMonitorOpInitiated, Values: []string{"1"}},
268 {Name: attrMonitorOpCompleted, Values: []string{"1"}},
269 },
270 },
271 {
272 DN: "cn=Unbind,cn=Operations,cn=Monitor",
273 Attributes: []*ldap.EntryAttribute{
274 {Name: attrMonitorOpInitiated, Values: []string{"1"}},
275 {Name: attrMonitorOpCompleted, Values: []string{"1"}},
276 },
277 },
278 {
279 DN: "cn=Add,cn=Operations,cn=Monitor",
280 Attributes: []*ldap.EntryAttribute{
281 {Name: attrMonitorOpInitiated, Values: []string{"1"}},
282 {Name: attrMonitorOpCompleted, Values: []string{"1"}},
283 },
284 },
285 {
286 DN: "cn=Delete,cn=Operations,cn=Monitor",
287 Attributes: []*ldap.EntryAttribute{
288 {Name: attrMonitorOpInitiated, Values: []string{"1"}},
289 {Name: attrMonitorOpCompleted, Values: []string{"1"}},
290 },
291 },
292 {
293 DN: "cn=Modify,cn=Operations,cn=Monitor",
294 Attributes: []*ldap.EntryAttribute{
295 {Name: attrMonitorOpInitiated, Values: []string{"1"}},
296 {Name: attrMonitorOpCompleted, Values: []string{"1"}},
297 },
298 },
299 {
300 DN: "cn=Compare,cn=Operations,cn=Monitor",
301 Attributes: []*ldap.EntryAttribute{
302 {Name: attrMonitorOpInitiated, Values: []string{"1"}},
303 {Name: attrMonitorOpCompleted, Values: []string{"1"}},
304 },
305 },
306 {
307 DN: "cn=Search,cn=Operations,cn=Monitor",
308 Attributes: []*ldap.EntryAttribute{
309 {Name: attrMonitorOpInitiated, Values: []string{"1"}},
310 {Name: attrMonitorOpCompleted, Values: []string{"1"}},
311 },
312 },
313 },
314 },
315 }
316 }
317
318 func prepareMockErrOnConnect() *mockOpenLDAPConn {
319 return &mockOpenLDAPConn{
320 errOnConnect: true,
321 }
322 }
323
324 func prepareMockErrOnSearch() *mockOpenLDAPConn {
325 return &mockOpenLDAPConn{
326 errOnSearch: true,
327 }
328 }
329
330 type mockOpenLDAPConn struct {
331 errOnConnect bool
332 disconnectCalled bool
333
334 dataSearchMonCounters *ldap.SearchResult
335 dataSearchMonOperations *ldap.SearchResult
336 errOnSearch bool
337 }
338
339 func (m *mockOpenLDAPConn) connect() error {
340 if m.errOnConnect {
341 return errors.New("mock.connect() error")
342 }
343 return nil
344 }
345
346 func (m *mockOpenLDAPConn) disconnect() error {
347 m.disconnectCalled = true
348 return nil
349 }
350
351 func (m *mockOpenLDAPConn) search(req *ldap.SearchRequest) (*ldap.SearchResult, error) {
352 if m.errOnSearch {
353 return nil, errors.New("mock.search() error")
354 }
355
356 switch req.BaseDN {
357 case "cn=Monitor":
358 return m.dataSearchMonCounters, nil
359 case "cn=Operations,cn=Monitor":
360 return m.dataSearchMonOperations, nil
361 default:
362 return nil, fmt.Errorf("mock.search(): unknown BaseDSN: %s", req.BaseDN)
363 }
364 }